Installation
Added by DRT 2021-12-03
Updated by DRT 2023-11-29
source("getReqdPkgs.r")
pkgs <- c("SingleCellExperiment","scater","scran","Seurat","miloR",
"tidyverse","patchwork","igraph","ggplot2",
"ggrastr","msigdbr","clusterProfiler",
"RColorBrewer","cowplot","devtools")
extra_pkgs <- c("irlba","Matrix") # NOTE these must be installed from source to make code work; see next code block
invisible(getReqdPkgs(pkgs))
'getOption("repos")' replaces Bioconductor standard repositories, see 'help("repositories", package = "BiocManager")' for
details.
Replacement repositories:
CRAN: https://cran.rstudio.com/
All required CRAN and BioC packages have previously been installed
NOTE
As of 2023-11-29, the runPCA function in scater breaks due to change
in Matrix library; must downgrade to Matrix 1.6.1 Downloaded v 1.6.1
from https://cran.r-project.org/src/contrib/Archive/Matrix/
installing from source (compiling on your computer, rather than
installing from a binary) may also be needed. On a Mac, this requires
gfortran to be installed. See here and link
to download gfortran 12.2
See this link: https://github.com/bwlewis/irlba/issues/70
# install.packages("irlba", type = "source")
# install.packages("Matrix", type = "source") # , version="1.6.1"
library(scater)
Loading required package: SingleCellExperiment
Loading required package: SummarizedExperiment
Loading required package: MatrixGenerics
Loading required package: matrixStats
Attaching package: ‘MatrixGenerics’
The following objects are masked from ‘package:matrixStats’:
colAlls, colAnyNAs, colAnys, colAvgsPerRowSet, colCollapse, colCounts, colCummaxs, colCummins,
colCumprods, colCumsums, colDiffs, colIQRDiffs, colIQRs, colLogSumExps, colMadDiffs, colMads, colMaxs,
colMeans2, colMedians, colMins, colOrderStats, colProds, colQuantiles, colRanges, colRanks, colSdDiffs,
colSds, colSums2, colTabulates, colVarDiffs, colVars, colWeightedMads, colWeightedMeans,
colWeightedMedians, colWeightedSds, colWeightedVars, rowAlls, rowAnyNAs, rowAnys, rowAvgsPerColSet,
rowCollapse, rowCounts, rowCummaxs, rowCummins, rowCumprods, rowCumsums, rowDiffs, rowIQRDiffs, rowIQRs,
rowLogSumExps, rowMadDiffs, rowMads, rowMaxs, rowMeans2, rowMedians, rowMins, rowOrderStats, rowProds,
rowQuantiles, rowRanges, rowRanks, rowSdDiffs, rowSds, rowSums2, rowTabulates, rowVarDiffs, rowVars,
rowWeightedMads, rowWeightedMeans, rowWeightedMedians, rowWeightedSds, rowWeightedVars
Loading required package: GenomicRanges
Loading required package: stats4
Loading required package: BiocGenerics
Attaching package: ‘BiocGenerics’
The following objects are masked from ‘package:stats’:
IQR, mad, sd, var, xtabs
The following objects are masked from ‘package:base’:
anyDuplicated, aperm, append, as.data.frame, basename, cbind, colnames, dirname, do.call, duplicated,
eval, evalq, Filter, Find, get, grep, grepl, intersect, is.unsorted, lapply, Map, mapply, match, mget,
order, paste, pmax, pmax.int, pmin, pmin.int, Position, rank, rbind, Reduce, rownames, sapply, setdiff,
sort, table, tapply, union, unique, unsplit, which.max, which.min
Loading required package: S4Vectors
Attaching package: ‘S4Vectors’
The following object is masked from ‘package:utils’:
findMatches
The following objects are masked from ‘package:base’:
expand.grid, I, unname
Loading required package: IRanges
Loading required package: GenomeInfoDb
Loading required package: Biobase
Welcome to Bioconductor
Vignettes contain introductory material; view with 'browseVignettes()'. To cite Bioconductor, see
'citation("Biobase")', and for packages 'citation("pkgname")'.
Attaching package: ‘Biobase’
The following object is masked from ‘package:MatrixGenerics’:
rowMedians
The following objects are masked from ‘package:matrixStats’:
anyMissing, rowMedians
Loading required package: scuttle
Loading required package: ggplot2
library(scran)
library(miloR)
Loading required package: edgeR
Loading required package: limma
Attaching package: ‘limma’
The following object is masked from ‘package:scater’:
plotMDS
The following object is masked from ‘package:BiocGenerics’:
plotMA
Attaching package: ‘edgeR’
The following object is masked from ‘package:SingleCellExperiment’:
cpm
library(tidyverse)
── Attaching core tidyverse packages ──────────────────────────────────────────────────────────────────── tidyverse 2.0.0 ──
✔ dplyr 1.1.4 ✔ readr 2.1.4
✔ forcats 1.0.0 ✔ stringr 1.5.1
✔ lubridate 1.9.3 ✔ tibble 3.2.1
✔ purrr 1.0.2 ✔ tidyr 1.3.0── Conflicts ────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ lubridate::%within%() masks IRanges::%within%()
✖ dplyr::collapse() masks IRanges::collapse()
✖ dplyr::combine() masks Biobase::combine(), BiocGenerics::combine()
✖ dplyr::count() masks matrixStats::count()
✖ dplyr::desc() masks IRanges::desc()
✖ tidyr::expand() masks S4Vectors::expand()
✖ dplyr::filter() masks stats::filter()
✖ dplyr::first() masks S4Vectors::first()
✖ dplyr::lag() masks stats::lag()
✖ ggplot2::Position() masks BiocGenerics::Position(), base::Position()
✖ purrr::reduce() masks GenomicRanges::reduce(), IRanges::reduce()
✖ dplyr::rename() masks S4Vectors::rename()
✖ lubridate::second() masks S4Vectors::second()
✖ lubridate::second<-() masks S4Vectors::second<-()
✖ dplyr::slice() masks IRanges::slice()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
library(patchwork)
library(igraph)
Attaching package: ‘igraph’
The following objects are masked from ‘package:lubridate’:
%--%, union
The following objects are masked from ‘package:dplyr’:
as_data_frame, groups, union
The following objects are masked from ‘package:purrr’:
compose, simplify
The following object is masked from ‘package:tidyr’:
crossing
The following object is masked from ‘package:tibble’:
as_data_frame
The following object is masked from ‘package:miloR’:
graph
The following object is masked from ‘package:scater’:
normalize
The following object is masked from ‘package:GenomicRanges’:
union
The following object is masked from ‘package:IRanges’:
union
The following object is masked from ‘package:S4Vectors’:
union
The following objects are masked from ‘package:BiocGenerics’:
normalize, path, union
The following objects are masked from ‘package:stats’:
decompose, spectrum
The following object is masked from ‘package:base’:
union
library(ggplot2)
library(ggrastr)
library(msigdbr)
library(cowplot)
Attaching package: ‘cowplot’
The following object is masked from ‘package:patchwork’:
align_plots
The following object is masked from ‘package:lubridate’:
stamp
library(Matrix)
Attaching package: ‘Matrix’
The following objects are masked from ‘package:tidyr’:
expand, pack, unpack
The following object is masked from ‘package:S4Vectors’:
expand
library(irlba)
library(RColorBrewer)
library(SingleCellExperiment)
Differential Abundance (DA) analysis with Milo
We test for differential abundance between healthy and cirrhotic
livers. We start by defining neighbourhoods with refined sampling on the
KNN graph. We inspect the size of neighbourhoods.
liver_milo <- Milo(liver_sce)
## Build KNN graph
liver_milo <- buildGraph(liver_milo, d = 11, k=30)
Constructing kNN graph with k:30
## Compute neighbourhoods with refined sampling
liver_milo <- makeNhoods(liver_milo, k=30, d=11, prop = 0.05, refined=TRUE)
Checking valid object
Running refined sampling with reduced_dim
plotNhoodSizeHist(liver_milo, bins=150)

Then we make a design matrix for the differential test, assigning
samples to biological conditions.
colData(liver_milo)[['sort']] <- str_remove(colData(liver_milo)[['dataset']], ".+_")
colData(liver_milo)[['sort']] <- str_remove(colData(liver_milo)[['sort']], "A|B")
liver_meta <- as_tibble(colData(liver_milo)[,c("dataset","condition", 'sort')])
liver_meta <- distinct(liver_meta) %>%
mutate(condition=factor(condition, levels=c("Uninjured", "Cirrhotic"))) %>%
column_to_rownames("dataset")
Now we can count cells in neighbourhoods and perform the DA test.
NOTE: Executing calcNhoodDistance() function takes a
long time (>30 min)
liver_milo <- countCells(liver_milo, samples = "dataset", meta.data = data.frame(colData(liver_milo)[,c("dataset","condition",'sort')]) )
Checking meta.data validity
Counting cells in neighbourhoods
liver_milo <- calcNhoodDistance(liver_milo, d=11)
'as(<dgTMatrix>, "dgCMatrix")' is deprecated.
Use 'as(., "CsparseMatrix")' instead.
See help("Deprecated") and help("Matrix-deprecated").
milo_res <- testNhoods(liver_milo, design = ~ condition, design.df = liver_meta[colnames(nhoodCounts(liver_milo)),])
Using TMM normalisation
Performing spatial FDR correction withk-distance weighting
milo_res_sort <- testNhoods(liver_milo, design = ~ sort + condition, design.df =
liver_meta[colnames(nhoodCounts(liver_milo)),])
Using TMM normalisation
Performing spatial FDR correction withk-distance weighting
compare_da_df <- left_join(milo_res_sort, milo_res, by="Nhood", suffix=c("_sort", "_nosort")) %>%
{annotateNhoods(liver_milo, ., 'annotation_lineage')}
compare_da_df %>%
ggplot(aes(-log10(SpatialFDR_sort), -log10(SpatialFDR_nosort))) +
geom_point(size=0.8) +
geom_point(data=. %>% filter(annotation_lineage=="Endothelia"), color="red")

plot(milo_res_sort$SpatialFDR, milo_res$SpatialFDR)

Exploration of Milo DA results
We can start by looking at some basic stats
pval_hist <- milo_res %>%
ggplot(aes(PValue)) +
geom_histogram(bins=50) +
theme_bw(base_size=14)
volcano <-
milo_res %>%
ggplot(aes(logFC, -log10(SpatialFDR))) +
geom_point(size=0.4, alpha=0.2) +
geom_hline(yintercept = -log10(0.1)) +
xlab("log-Fold Change") +
theme_bw(base_size=14)
pval_hist + volcano

The distribution of P-values looks sensible and from the volcano plot
we can see that we have identified some DA neighbourhoods at 10%
FDR.
We can visualize DA neighbourhoods building an abstracted graph.
NOTE: This takes a while to process! Can take >30
min.
liver_milo <- buildNhoodGraph(liver_milo)
plotNhoodGraphDA(liver_milo, milo_res, alpha = 0.1, size_range=c(2,6))

Note: This step is not required. It generates a large file (even
larger than raw data) and then reloads it.
## Save milo object and results
# saveRDS(liver_milo,"~/dropbox-vu/temp/milo_data/Ramachandran2019_liver/liver_milo_20210225.RDS")
# write_csv(milo_res,"~/dropbox-vu/temp/milo_data/Ramachandran2019_liver/liver_results_20210225.csv")
# liver_milo <- readRDS("~/liver_milo_20201008.RDS")
liver_milo <- readRDS(url("https://www.dropbox.com/s/xdp07789c5hoen3/liver_milo_20210225.RDS?dl=1"))
# milo_res <- read_csv("/nfs/team205/ed6/data/Ramachandran2019_liver/liver_results_20201008.csv")
milo_res <- read_csv(url("https://www.dropbox.com/s/i1l3aep1py5wirf/liver_results_20210225.csv?dl=1"))
Making figures for the manuscript
colourCount = length(unique(liver_milo$annotation_lineage))
getPalette = colorRampPalette(brewer.pal(9, "Set2"))
Warning: n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors
umap_df <- data.frame(reducedDim(liver_milo, "UMAP"))
colnames(umap_df) <- c("UMAP_1", "UMAP_2")
umap1 <- cbind(umap_df, annotation_lineage=liver_milo$annotation_lineage) %>%
ggplot(aes(UMAP_1, UMAP_2, color=as.character(annotation_lineage))) +
geom_point_rast(size=0.1, alpha=0.5, raster.dpi = 800) +
ggrepel::geom_text_repel(data = . %>%
group_by(annotation_lineage) %>%
summarise(UMAP_1=mean(UMAP_1), UMAP_2=mean(UMAP_2)),
aes(label=annotation_lineage), color="black", size=6
) +
scale_color_manual(values=getPalette(colourCount)) +
guides(color="none") +
xlab("UMAP1") + ylab("UMAP2") +
coord_fixed() +
theme_classic(base_size = 22) +
theme(axis.text = element_blank(), axis.ticks = element_blank())
fn_liver_umap1 <- file.path(OUTPUT_DIR,"/liver_v2/liver_umap1_new.pdf")
if(OVERWRITE | !file.exists(fn_liver_umap1)) {
ggsave(fn_liver_umap1, height = 7, width = 8)
} else {
message(cat(fn_liver_umap1,"exists and will not be overwritten\n"))
}
~/dropbox-vu/temp/milo_output//liver_v2/liver_umap1_new.pdf exists and will not be overwritten
umap2 <-
cbind(umap_df, condition=as.character(liver_milo$condition)) %>%
ggplot(aes(UMAP_1, UMAP_2, color=condition)) +
geom_point_rast(size=0.1, alpha=0.5, raster.dpi = 800) +
scale_color_brewer(palette="Set1", name='') +
xlab("UMAP1") + ylab("UMAP2") +
coord_fixed() +
guides(color='none') +
facet_wrap(condition~., ncol=1) +
theme_nothing(font_size = 22) +
theme(axis.text = element_blank(), axis.ticks = element_blank(), legend.position=c(0.9,0.9),
strip.background = element_rect(color=NA), strip.text = element_text(size=22))
fn_liver_umap2 <- file.path(OUTPUT_DIR,"/liver_v2/liver_umap2_new.pdf")
if(OVERWRITE | !file.exists(fn_liver_umap1)) {
ggsave(fn_liver_umap2, height = 7, width = 8)
} else {
message(cat(fn_liver_umap2,"exists and will not be overwritten\n"))
}
~/dropbox-vu/temp/milo_output//liver_v2/liver_umap2_new.pdf exists and will not be overwritten
nh_graph_pl <- plotNhoodGraphDA(liver_milo, milo_res, alpha = 0.1, size_range=c(1,4)) +
theme(legend.text = element_text(size=20), legend.title = element_text(size=22)) +
coord_fixed()
fn_nh_graph <- file.path(OUTPUT_DIR,"/liver_v2/liver_graph_new.pdf")
if(OVERWRITE | !file.exists(fn_nh_graph)) {
ggsave(fn_nh_graph, height = 7, width = 8)
} else {
message(cat(fn_nh_graph,"exists and will not be overwritten\n"))
}
~/dropbox-vu/temp/milo_output//liver_v2/liver_graph_new.pdf exists and will not be overwritten
fig4_top <- (umap1 | umap2 | nh_graph_pl) +
plot_layout(widths = c(3,1,3))
fig4_top

Explore DA neighbourhoods by cell type
Next, we can check the cell types where we observe most differences
between healthy and cirrhotic cells, by taking the most frequent cell
type in each neighbourhood.
milo_res <- milo_res[,!str_detect(colnames(milo_res), "annotation_lineage")]
# Add annotation of most frequent cell type per nhood to milo results table
milo_res <- annotateNhoods(liver_milo, milo_res, "annotation_indepth")
anno_df <- data.frame(liver_milo@colData) %>%
distinct(annotation_lineage, annotation_indepth)
milo_res <- left_join(milo_res, anno_df, by="annotation_indepth")
We first check that neighbourhoods are sufficiently homogeneous
frac_hist <- ggplot(milo_res, aes(annotation_indepth_fraction)) +
geom_histogram(bins=30) +
xlab("Fraction of cells in \nmost abundant cluster") +
ylab("# neighbourhoods") +
theme_bw(base_size=14)
frac_hist

Filter nhoods with homogeneous composition
milo_res$annotation_indepth[milo_res$annotation_indepth_fraction < 0.6] <- NA
milo_res$annotation_lineage[milo_res$annotation_indepth_fraction < 0.6] <- NA
We can recover all the clusters where DA was detected in the original
paper
group.by = "annotation_indepth"
paper_DA <- list(cirrhotic=c("MPs (4)","MPs (5)",
"Endothelia (6)", "Endothelia (7)",
"Mes (3)",
"Tcells (2)",
"Myofibroblasts"
),
healthy=c("MPs (7)",
"Endothelia (1)",
"Tcells (1)", "Tcells (3)","Tcells (1)",
"ILCs (1)"
)
)
expDA_df <- bind_rows(
data.frame(annotation_indepth = paper_DA[["cirrhotic"]], pred_DA="cirrhotic"),
data.frame(annotation_indepth = paper_DA[["healthy"]], pred_DA="healthy")
)
pl1 <- milo_res %>%
left_join(expDA_df) %>%
mutate(is_signif = ifelse(SpatialFDR < 0.1, 1, 0)) %>%
mutate(logFC_color = ifelse(is_signif==1, logFC, NA)) %>%
arrange(annotation_lineage) %>%
mutate(Nhood=factor(Nhood, levels=unique(Nhood))) %>%
filter(!is.na(annotation_lineage)) %>%
ggplot(aes(annotation_indepth, logFC, color=logFC_color)) +
scale_color_gradient2() +
guides(color="none") +
xlab(group.by) + ylab("Log Fold Change") +
ggbeeswarm::geom_quasirandom(alpha=1) +
coord_flip() +
facet_grid(annotation_lineage~., scales="free", space="free") +
theme_bw(base_size=22) +
theme(strip.text.y = element_text(angle=0),
axis.title.y = element_blank(), axis.text.y = element_blank(), axis.ticks.y = element_blank(),
)
pl2 <- milo_res %>%
left_join(expDA_df) %>%
# dplyr::filter(!is.na(pred_DA)) %>%
group_by(annotation_indepth) %>%
summarise(pred_DA=dplyr::first(pred_DA), annotation_lineage=dplyr::first(annotation_lineage)) %>%
mutate(end=ifelse(pred_DA=="healthy", 0, 1),
start=ifelse(pred_DA=="healthy", 1, 0)) %>%
filter(!is.na(annotation_lineage)) %>%
ggplot(aes(annotation_indepth, start, xend = annotation_indepth, yend = end, color=pred_DA)) +
geom_segment(size=1,arrow=arrow(length = unit(0.1, "npc"), type="closed")) +
coord_flip() +
xlab("annotation") +
facet_grid(annotation_lineage~.,
# annotation_lineage~"Ramachandran et al.\nDA predictions",
scales="free", space="free") +
# guides(color="none") +
scale_color_brewer(palette="Set1", direction = -1,
labels=c("enriched in cirrhotic", "enriched in healthy"),
na.translate = F,
name="Ramachandran et al.\nDA predictions") +
guides(color=guide_legend(ncol = 1)) +
theme_bw(base_size=22) +
ylim(-0.1,1.1) +
theme(strip.text.y = element_blank(),strip.text.x = element_text(angle=90),
plot.margin = unit(c(0,0,0,0), "cm"), panel.grid = element_blank(),
axis.title.x = element_blank(), axis.text.x = element_blank(), axis.ticks.x = element_blank(),
legend.position = "bottom")
fig4_bleft <- (pl2 + pl1 +
plot_layout(widths=c(1,10), guides = "collect") & theme(legend.position = 'top', legend.justification = 0))
fn_liver_DAcomp <- file.path(OUTPUT_DIR,"/liver_v2/liver_DAcomparison.pdf")
if(OVERWRITE | !file.exists(fn_liver_DAcomp)) {
ggsave(fn_liver_DAcomp, height = 13, width = 8)
} else {
message(cat(fn_liver_DAcomp,"exists and will not be overwritten\n"))
}
~/dropbox-vu/temp/milo_output//liver_v2/liver_DAcomparison.pdf exists and will not be overwritten
# ggsave("~/mount/gdrive/milo/Figures/liver_v2/liver_DAcomparison.pdf", width=8, height = 13)
# ggsave("~/dropbox-vu/temp/milo_output/liver_v2/liver_DAcomparison.pdf", width=8, height = 13)
fig4_bleft

Close-up on Endothelial lineage
endo_milo <- scater::runUMAP(liver_milo[,liver_milo$annotation_lineage=="Endothelia"], dimred='PCA')
Found more than one class "dist" in cache; using the first, from namespace 'BiocGenerics'
Also defined by ‘spam’
Found more than one class "dist" in cache; using the first, from namespace 'BiocGenerics'
Also defined by ‘spam’
plotUMAP(endo_milo, colour_by = "annotation_indepth")

umap_df <- data.frame(reducedDim(endo_milo, "UMAP"))
colnames(umap_df) <- c("UMAP_1", "UMAP_2")
endo_umap <- cbind(umap_df, condition=endo_milo$condition) %>%
ggplot(aes(UMAP_1, UMAP_2, color=condition)) +
geom_point(size=0.3, alpha=0.5) +
scale_color_brewer(palette="Set1", name='') +
xlab("UMAP1") + ylab("UMAP2") +
coord_fixed() +
guides(color="none") +
facet_wrap(condition~., ncol=1) +
theme_nothing() +
theme(axis.text = element_blank(), axis.ticks = element_blank(), legend.position=c(0.9,0.9),
strip.background = element_rect(color=NA), strip.text = element_text(size=22))
liver_milo2 <- liver_milo
subset.nhoods <- str_detect(milo_res$annotation_indepth, "Endo")
reducedDim(liver_milo2, "UMAP")[colnames(endo_milo),] <- reducedDim(endo_milo, "UMAP")
endo_gr <-
plotNhoodGraphDA(
liver_milo2, milo_res,
subset.nhoods = which(milo_res$annotation_lineage == "Endothelia"),
size_range=c(1,4),
# ) =)[1:(length()-1)],
alpha = 0.1
) +
theme(legend.text = element_text(size=20), legend.title = element_text(size=22))
fig4_bright1 <- ((endo_umap + endo_gr ) +
plot_layout(widths = c(1,2),
guides = "collect"
))
fig4_bright1

Can’t get code below to run
Unlcear what the problem is; data structure?
# # nh_graph <- nhoodGraph(liver_milo)[subset.nhoods,subset.nhoods]
#
# nh_graph <- nhoodGraph(liver_milo)[na.omit(subset.nhoods)]
#
# nh_graph <- graph_from_adjacency_matrix(nh_graph)
#
# col_vals <- colData(liver_milo)[as.numeric(vertex_attr(nh_graph)$name), colour_by]
# V(nh_graph)$colour_by <- ifelse(milo_res[subset.nhoods,"SpatialFDR"] > 0.1, 0, milo_res[subset.nhoods,"logFC"])
# ggraph(simplify(nh_graph)) +
# geom_edge_link0(edge_colour = "grey66", edge_alpha=0.2) +
# geom_node_point(aes(fill = colour_by), shape=21, size=2) +
# scale_fill_gradient2()
Close-up on Cholangiocytes
chol_milo <- scater::runUMAP(liver_milo[,liver_milo$annotation_lineage=="Cholangiocytes"], dimred='PCA')
Found more than one class "dist" in cache; using the first, from namespace 'BiocGenerics'
Also defined by ‘spam’
Found more than one class "dist" in cache; using the first, from namespace 'BiocGenerics'
Also defined by ‘spam’
plotUMAP(chol_milo, colour_by = "annotation_indepth")

plotUMAP(chol_milo, colour_by = "percent.mito")

Filter out cells that show contamination from immune cells
(expression of immune markers)
keep <- logcounts(chol_milo)["CD19",] == 0 | logcounts(chol_milo)["MS4A1",] == 0
chol_milo <- chol_milo[,keep]
chol_milo <- scater::runUMAP(chol_milo, dimred='PCA')
Found more than one class "dist" in cache; using the first, from namespace 'BiocGenerics'
Also defined by ‘spam’
Found more than one class "dist" in cache; using the first, from namespace 'BiocGenerics'
Also defined by ‘spam’
plotUMAP(chol_milo, colour_by = "annotation_indepth")

umap_df <- data.frame(reducedDim(chol_milo, "UMAP"))
colnames(umap_df) <- c("UMAP_1", "UMAP_2")
chol_umap <- cbind(umap_df, condition=chol_milo$condition) %>%
ggplot(aes(UMAP_1, UMAP_2, color=condition)) +
geom_point(size=0.3, alpha=0.5) +
scale_color_brewer(palette="Set1", name='') +
xlab("UMAP1") + ylab("UMAP2") +
coord_fixed() +
guides(color="none") +
facet_wrap(condition~., ncol=1) +
theme_nothing() +
theme(axis.text = element_blank(), axis.ticks = element_blank(), legend.position=c(0.9,0.9),
strip.background = element_rect(color=NA), strip.text = element_text(size=22))
chol_umap

liver_milo2 <- liver_milo
subset.nhoods <- milo_res$annotation_lineage=="Cholangiocytes"
reducedDim(liver_milo2, "UMAP")[colnames(chol_milo),] <- reducedDim(chol_milo, "UMAP")
chol_gr <-
plotNhoodGraphDA(
liver_milo2, milo_res,
subset.nhoods = subset.nhoods,
size_range=c(2,5),
# ) =)[1:(length()-1)],
alpha = 0.1
) +
theme(legend.text = element_text(size=22), legend.title = element_text(size=24))
(chol_umap + chol_gr ) +
plot_layout(widths = c(1,2),
guides = "collect"
)

# fig4_bright1 +
# ggsave("~/milo_output/liver_endoGraph.pdf", width=9, height = 5)
# ggsave("~/dropbox-vu/temp/milo_output/liver_endoGraph.pdf", width=9, height = 5)
Differential Gene Expression analysis
In a subset of lineages, we want to test for differential expression
between neighbourhoods enriched in cirrhotic cells and neighbourhoods
enriched.
Add nhood expression to speed-up plotting of heatmaps
liver_milo <- calcNhoodExpression(liver_milo, assay = "logcounts", subset.row = hvgs)
| # DRT: stopping run-through here |
| Should be able to run everything above. |
Endothelia
Rebuttal figure showcasing grouping
set.seed(42)
milo_res_endogroups <- groupNhoods(liver_milo, milo_res, max.lfc.delta = 2, overlap = 1)
Found 1385 DA neighbourhoods at FDR 10%
nhoodAdjacency found - using for nhood grouping
p1 <- plotNhoodGroups(liver_milo, milo_res_endogroups,
size_range=c(1,3))
milo_res_endogroups <- annotateNhoods(liver_milo, milo_res_endogroups, 'annotation_lineage')
p2 <- plotDAbeeswarm(milo_res_endogroups, group.by = 'NhoodGroup') +
facet_grid(annotation_lineage~., scales="free", space="free")
Converting group_by to factor...
## Plot expression in T cell neighbourhoods
# markers_df <- read_csv("~/mount/gdrive/milo/STable3_Ramachandran.csv")
tcell_marker_genes <-
markers_df %>%
filter(cluster %in% c("Tcell", "ILC")) %>%
top_n(30, myAUC) %>%
pull(gene)
Error: object 'markers_df' not found
Group endothelial cells by logFC and DA results
Calculate marker genes between the two groups
Visualize as volcano
Visualize as heatmap
(gene expression values are scaled between 0 and 1 for each gene)
GO term analysis
LS0tCnRpdGxlOiAiTWlsbzogbGl2ZXIgY2lycmhvc2lzIGFuYWx5c2lzIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKLS0tCgojIyBJbnRyb2R1Y3Rpb24KCkluIHRoaXMgbm90ZWJvb2sgd2UgZGVtb25zdHJhdGUgaG93IHRvIHVzZSBNaWxvIHRvIGRldGVjdCBhYmhlcnJhbnQgY2VsbCBzdGF0ZXMgaW4gZGlzZWFzZWQgdGlzc3VlcywgdXNpbmcgYSBkYXRhc2V0IG9mIGhlcGF0aWMgbm9uLXBhcmVuY2h5bWFsIGNlbGxzIGlzb2xhdGVkIGZyb20gNSBoZWFsdGh5IGFuZCA1IGNpcnJob3RpYyBodW1hbiBsaXZlcnMuIFtSYW1hY2hhbmRyYW4gZXQgYWwuIDIwMTldKGh0dHBzOi8vd3d3Lm5hdHVyZS5jb20vYXJ0aWNsZXMvczQxNTg2LTAxOS0xNjMxLTMjU2VjMSkgKEdFTyBhY2Nlc3NpaW9uOiBHU0UxMzYxMDMpLgoKIyMgSW5zdGFsbGF0aW9uCiMjIyMgQWRkZWQgYnkgRFJUIDIwMjEtMTItMDMKIyMjIyBVcGRhdGVkIGJ5IERSVCAyMDIzLTExLTI5CmBgYHtyIEluc3RhbGxhdGlvbn0Kc291cmNlKCJnZXRSZXFkUGtncy5yIikKCnBrZ3MgPC0gYygiU2luZ2xlQ2VsbEV4cGVyaW1lbnQiLCJzY2F0ZXIiLCJzY3JhbiIsIlNldXJhdCIsIm1pbG9SIiwKICAidGlkeXZlcnNlIiwicGF0Y2h3b3JrIiwiaWdyYXBoIiwiZ2dwbG90MiIsCiAgImdncmFzdHIiLCJtc2lnZGJyIiwiY2x1c3RlclByb2ZpbGVyIiwKICAiUkNvbG9yQnJld2VyIiwiY293cGxvdCIsImRldnRvb2xzIikKCmV4dHJhX3BrZ3MgPC0gYygiaXJsYmEiLCJNYXRyaXgiKSAjIE5PVEUgdGhlc2UgbXVzdCBiZSBpbnN0YWxsZWQgZnJvbSBzb3VyY2UgdG8gbWFrZSBjb2RlIHdvcms7IHNlZSBuZXh0IGNvZGUgYmxvY2sKCmludmlzaWJsZShnZXRSZXFkUGtncyhwa2dzKSkKYGBgCgoKIyMjIE5PVEUKQXMgb2YgMjAyMy0xMS0yOSwgdGhlIHJ1blBDQSBmdW5jdGlvbiBpbiBzY2F0ZXIgYnJlYWtzIGR1ZSB0byBjaGFuZ2UgaW4gTWF0cml4IGxpYnJhcnk7IG11c3QgZG93bmdyYWRlIHRvIE1hdHJpeCAxLjYuMQpEb3dubG9hZGVkIHYgMS42LjEgZnJvbSBodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy9zcmMvY29udHJpYi9BcmNoaXZlL01hdHJpeC8gIAoKaW5zdGFsbGluZyBmcm9tIHNvdXJjZSAoY29tcGlsaW5nIG9uIHlvdXIgY29tcHV0ZXIsIHJhdGhlciB0aGFuIGluc3RhbGxpbmcgZnJvbSBhIGJpbmFyeSkgbWF5IGFsc28gYmUgbmVlZGVkLiBPbiBhIE1hYywgdGhpcyByZXF1aXJlcyBnZm9ydHJhbiB0byBiZSBpbnN0YWxsZWQuICBTZWUgW2hlcmVdKGh0dHBzOi8vbWFjLnItcHJvamVjdC5vcmcvdG9vbHMvKSBhbmQgW2xpbmsgdG8gZG93bmxvYWQgZ2ZvcnRyYW4gMTIuMl0oaHR0cHM6Ly9tYWMuci1wcm9qZWN0Lm9yZy90b29scy9nZm9ydHJhbi0xMi4yLXVuaXZlcnNhbC5wa2cpICAKClNlZSB0aGlzIGxpbms6IGh0dHBzOi8vZ2l0aHViLmNvbS9id2xld2lzL2lybGJhL2lzc3Vlcy83MAoKYGBge3J9CiMgaW5zdGFsbC5wYWNrYWdlcygiaXJsYmEiLCB0eXBlID0gInNvdXJjZSIpCiMgaW5zdGFsbC5wYWNrYWdlcygiTWF0cml4IiwgdHlwZSA9ICJzb3VyY2UiKSAjICwgdmVyc2lvbj0iMS42LjEiCmBgYAoKYGBge3J9CmxpYnJhcnkoc2NhdGVyKQpsaWJyYXJ5KHNjcmFuKQpsaWJyYXJ5KG1pbG9SKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkoaWdyYXBoKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ2dyYXN0cikKbGlicmFyeShtc2lnZGJyKQpsaWJyYXJ5KGNvd3Bsb3QpCmxpYnJhcnkoTWF0cml4KQpsaWJyYXJ5KGlybGJhKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeShTaW5nbGVDZWxsRXhwZXJpbWVudCkKYGBgCiMjIERlZmluZSBzb21lIGdsb2JhbCB2YXJpYWJsZXMKTG9jYXRpb24gdG8gc2F2ZSBvdXRwdXQgZmlsZXMgYW5kIHdoZXRoZXIgdG8gb3ZlcndyaXRlIG91dHB1dCBmaWxlcy4KYGBge3J9Ck9VVFBVVF9ESVIgPC0gIn4vZHJvcGJveC12dS90ZW1wL21pbG9fb3V0cHV0IgpPVkVSV1JJVEUgPC0gRkFMU0UKYGBgCgoKIyMgTG9hZCBkYXRhCgpXZSBkb3dubG9hZGVkIHRoZSBkYXRhc2V0IGFuZCBhbm5vdGF0aW9ucyBzdG9yZWQgaW4gU2V1cmF0IG9iamVjdCBmcm9tIFtoZXJlXShodHRwczovL2RhdGFzaGFyZS5pcy5lZC5hYy51ay9oYW5kbGUvMTAyODMvMzQzMyksIGFzIGluZGljYXRlZCBieSB0aGUgYXV0aG9ycy4gVGhlIGZpbGVzIGFyZSBvbiBEcm9wQm94IGFuZCBjYW4gYmUgYWNjZXNzZWQgZGlyZWN0bHkgd2l0aCB0aGUgY29kZSBiZWxvdy4KCmBgYHtyfQojIGxvYWQoIi9uZnMvdGVhbTIwNS9lZDYvZGF0YS9SYW1hY2hhbmRyYW4yMDE5X2xpdmVyL3Rpc3N1ZS5yZGF0YSIpCmxvYWQodXJsKCJodHRwczovL3d3dy5kcm9wYm94LmNvbS9zL2JxODE2aDc0Z21oODRndS90aXNzdWUucmRhdGE/ZGw9MSIpKQojIyBDb252ZXJ0IHRvIFNpbmdsZUNlbGxFeHBlcmltZW50CmxpdmVyX3NjZSA8LSBTaW5nbGVDZWxsRXhwZXJpbWVudChhc3NheSA9IGxpc3QoY291bnRzPXRpc3N1ZUByYXcuZGF0YSwgbG9nY291bnRzPXRpc3N1ZUBkYXRhKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbERhdGEgPSB0aXNzdWVAbWV0YS5kYXRhKQoKbGl2ZXJfc2NlCmBgYAoKIyMgUHJlcHJvY2Vzc2luZwoKV2UgdXNlIHRoZSBzYW1lIG51bWJlciBvZiBoaWdobHkgdmFyaWFibGUgZ2VuZXMgYW5kIHByaW5jaXBhbCBjb21wb25lbnRzIHVzZWQgYnkgdGhlIGF1dGhvcnMgb2YgdGhlIG9yaWdpbmFsIHN0dWR5LiAKCiMjIyBGZWF0dXJlIHNlbGVjdGlvbgoKU2VsZWN0IGhpZ2hseSB2YXJpYWJsZSBnZW5lcwoKYGBge3IgU2VsZWN0IGh2Z3N9CmRlY19saXZlciA8LSBtb2RlbEdlbmVWYXIobGl2ZXJfc2NlKQoKZml0X2xpdmVyIDwtIG1ldGFkYXRhKGRlY19saXZlcikKcGxvdChmaXRfbGl2ZXIkbWVhbiwgZml0X2xpdmVyJHZhciwgeGxhYj0iTWVhbiBvZiBsb2ctZXhwcmVzc2lvbiIsCiAgICB5bGFiPSJWYXJpYW5jZSBvZiBsb2ctZXhwcmVzc2lvbiIpCgpodmdzIDwtIGdldFRvcEhWR3MoZGVjX2xpdmVyLCBuPTMwMDApCmBgYAoKIyMjIERpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbgoKYGBge3IsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTh9CnNldC5zZWVkKDQyKQpsaXZlcl9zY2UgPC0gcnVuUENBKGxpdmVyX3NjZSwgc3Vic2V0X3Jvdz1odmdzLCBuY29tcG9uZW50cz0xMSkKbGl2ZXJfc2NlIDwtIHJ1blVNQVAobGl2ZXJfc2NlLCBkaW1yZWQ9IlBDQSIsIG5jb21wb25lbnRzPTIpCgpzY2F0ZXI6OnBsb3RVTUFQKGxpdmVyX3NjZSwgY29sb3VyX2J5PSJjb25kaXRpb24iLCBwb2ludF9hbHBoYT0xLCAgcG9pbnRfc2l6ZT0wLjUpCnNjYXRlcjo6cGxvdFVNQVAobGl2ZXJfc2NlLCBjb2xvdXJfYnk9ImRhdGFzZXQiLCBwb2ludF9hbHBoYT0wLjMsICBwb2ludF9zaXplPTAuNSkKc2NhdGVyOjpwbG90VU1BUChsaXZlcl9zY2UsIGNvbG91cl9ieT0iYW5ub3RhdGlvbl9saW5lYWdlIiwgcG9pbnRfYWxwaGE9MC4zLCAgcG9pbnRfc2l6ZT0wLjUsIHRleHRfYnk9J2Fubm90YXRpb25fbGluZWFnZScpCmBgYAoKTm90YWJseSwgdGhpcyBkYXRhc2V0IGRvZXNuJ3QgYXBwZWFyIHRvIGRpc3BsYXkgYSBiYXRjaCBlZmZlY3QKCiMjIyBOb3RlOiBUaGlzIHN0ZXAgaXMgbm90IHJlcXVpcmVkLiBJdCBnZW5lcmF0ZXMgYSBsYXJnZSBmaWxlIChldmVuIGxhcmdlciB0aGFuIHJhdyBkYXRhKSBhbmQgdGhlbiByZWxvYWRzIGl0LiAKYGBge3Igc2F2ZS1sb2FkIGludGVybWVkIGZpbGUgMSwgZXZhbD1GQUxTRSwgaW5jbHVkZT1UUlVFLCBlY2hvPVRSVUV9CiMgc2F2ZVJEUyhsaXZlcl9zY2UsICJ+L2Ryb3Bib3gtdnUvdGVtcC9taWxvX2RhdGEvUmFtYWNoYW5kcmFuMjAxOV9saXZlci9saXZlcl9TQ0VfMjAyMTAyMjUuUkRTIikKIyBsaXZlcl9zY2UgPC0gcmVhZFJEUyh1cmwoImh0dHBzOi8vd3d3LmRyb3Bib3guY29tL3MvejBhb3BmNjE2YjF1cnZqL2xpdmVyX1NDRV8yMDIxMDIyNS5SRFM/ZGw9MSIpKQpgYGAKCiMjIERpZmZlcmVudGlhbCBBYnVuZGFuY2UgKERBKSBhbmFseXNpcyB3aXRoIE1pbG8KCldlIHRlc3QgZm9yIGRpZmZlcmVudGlhbCBhYnVuZGFuY2UgYmV0d2VlbiBoZWFsdGh5IGFuZCBjaXJyaG90aWMgbGl2ZXJzLiBXZSBzdGFydCBieSBkZWZpbmluZyBuZWlnaGJvdXJob29kcyB3aXRoIHJlZmluZWQgc2FtcGxpbmcgb24gdGhlIEtOTiBncmFwaC4gV2UgaW5zcGVjdCB0aGUgc2l6ZSBvZiBuZWlnaGJvdXJob29kcy4KCmBgYHtyfQpsaXZlcl9taWxvIDwtIE1pbG8obGl2ZXJfc2NlKQoKIyMgQnVpbGQgS05OIGdyYXBoCmxpdmVyX21pbG8gPC0gYnVpbGRHcmFwaChsaXZlcl9taWxvLCBkID0gMTEsIGs9MzApCgojIyBDb21wdXRlIG5laWdoYm91cmhvb2RzIHdpdGggcmVmaW5lZCBzYW1wbGluZwpsaXZlcl9taWxvIDwtIG1ha2VOaG9vZHMobGl2ZXJfbWlsbywgaz0zMCwgZD0xMSwgcHJvcCA9IDAuMDUsIHJlZmluZWQ9VFJVRSkKcGxvdE5ob29kU2l6ZUhpc3QobGl2ZXJfbWlsbywgYmlucz0xNTApCmBgYAoKVGhlbiB3ZSBtYWtlIGEgZGVzaWduIG1hdHJpeCBmb3IgdGhlIGRpZmZlcmVudGlhbCB0ZXN0LCBhc3NpZ25pbmcgc2FtcGxlcyB0byBiaW9sb2dpY2FsIGNvbmRpdGlvbnMuCgpgYGB7cn0KY29sRGF0YShsaXZlcl9taWxvKVtbJ3NvcnQnXV0gPC0gc3RyX3JlbW92ZShjb2xEYXRhKGxpdmVyX21pbG8pW1snZGF0YXNldCddXSwgIi4rXyIpCmNvbERhdGEobGl2ZXJfbWlsbylbWydzb3J0J11dIDwtIHN0cl9yZW1vdmUoY29sRGF0YShsaXZlcl9taWxvKVtbJ3NvcnQnXV0sICJBfEIiKQoKbGl2ZXJfbWV0YSA8LSBhc190aWJibGUoY29sRGF0YShsaXZlcl9taWxvKVssYygiZGF0YXNldCIsImNvbmRpdGlvbiIsICdzb3J0JyldKQpsaXZlcl9tZXRhIDwtIGRpc3RpbmN0KGxpdmVyX21ldGEpICU+JQogIG11dGF0ZShjb25kaXRpb249ZmFjdG9yKGNvbmRpdGlvbiwgbGV2ZWxzPWMoIlVuaW5qdXJlZCIsICJDaXJyaG90aWMiKSkpICU+JQogIGNvbHVtbl90b19yb3duYW1lcygiZGF0YXNldCIpCgpgYGAKCk5vdyB3ZSBjYW4gY291bnQgY2VsbHMgaW4gbmVpZ2hib3VyaG9vZHMgYW5kIHBlcmZvcm0gdGhlIERBIHRlc3QuICAKCk5PVEU6IEV4ZWN1dGluZyBgY2FsY05ob29kRGlzdGFuY2UoKWAgZnVuY3Rpb24gdGFrZXMgYSBsb25nIHRpbWUgKD4zMCBtaW4pCmBgYHtyfQpsaXZlcl9taWxvIDwtIGNvdW50Q2VsbHMobGl2ZXJfbWlsbywgc2FtcGxlcyA9ICJkYXRhc2V0IiwgbWV0YS5kYXRhID0gZGF0YS5mcmFtZShjb2xEYXRhKGxpdmVyX21pbG8pWyxjKCJkYXRhc2V0IiwiY29uZGl0aW9uIiwnc29ydCcpXSkgKQpsaXZlcl9taWxvIDwtIGNhbGNOaG9vZERpc3RhbmNlKGxpdmVyX21pbG8sIGQ9MTEpCm1pbG9fcmVzIDwtIHRlc3ROaG9vZHMobGl2ZXJfbWlsbywgZGVzaWduID0gfiBjb25kaXRpb24sIGRlc2lnbi5kZiA9IGxpdmVyX21ldGFbY29sbmFtZXMobmhvb2RDb3VudHMobGl2ZXJfbWlsbykpLF0pCm1pbG9fcmVzX3NvcnQgPC0gdGVzdE5ob29kcyhsaXZlcl9taWxvLCBkZXNpZ24gPSB+IHNvcnQgKyBjb25kaXRpb24sIGRlc2lnbi5kZiA9IAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpdmVyX21ldGFbY29sbmFtZXMobmhvb2RDb3VudHMobGl2ZXJfbWlsbykpLF0pCmBgYAoKYGBge3J9CmNvbXBhcmVfZGFfZGYgPC0gbGVmdF9qb2luKG1pbG9fcmVzX3NvcnQsIG1pbG9fcmVzLCBieT0iTmhvb2QiLCBzdWZmaXg9YygiX3NvcnQiLCAiX25vc29ydCIpKSAlPiUKICB7YW5ub3RhdGVOaG9vZHMobGl2ZXJfbWlsbywgLiwgJ2Fubm90YXRpb25fbGluZWFnZScpfSAKCmNvbXBhcmVfZGFfZGYgJT4lCiAgZ2dwbG90KGFlcygtbG9nMTAoU3BhdGlhbEZEUl9zb3J0KSwgLWxvZzEwKFNwYXRpYWxGRFJfbm9zb3J0KSkpICsKICBnZW9tX3BvaW50KHNpemU9MC44KSArCiAgZ2VvbV9wb2ludChkYXRhPS4gJT4lIGZpbHRlcihhbm5vdGF0aW9uX2xpbmVhZ2U9PSJFbmRvdGhlbGlhIiksIGNvbG9yPSJyZWQiKQpwbG90KG1pbG9fcmVzX3NvcnQkU3BhdGlhbEZEUiwgbWlsb19yZXMkU3BhdGlhbEZEUikKYGBgCgoKIyMgRXhwbG9yYXRpb24gb2YgTWlsbyBEQSByZXN1bHRzCgpXZSBjYW4gc3RhcnQgYnkgbG9va2luZyBhdCBzb21lIGJhc2ljIHN0YXRzCgpgYGB7cn0KcHZhbF9oaXN0IDwtIG1pbG9fcmVzICU+JQogIGdncGxvdChhZXMoUFZhbHVlKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbnM9NTApICsKICB0aGVtZV9idyhiYXNlX3NpemU9MTQpCgp2b2xjYW5vIDwtCiAgbWlsb19yZXMgJT4lCiAgZ2dwbG90KGFlcyhsb2dGQywgLWxvZzEwKFNwYXRpYWxGRFIpKSkgKwogIGdlb21fcG9pbnQoc2l6ZT0wLjQsIGFscGhhPTAuMikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC1sb2cxMCgwLjEpKSArCiAgeGxhYigibG9nLUZvbGQgQ2hhbmdlIikgKwogIHRoZW1lX2J3KGJhc2Vfc2l6ZT0xNCkKCnB2YWxfaGlzdCArIHZvbGNhbm8KYGBgCgpUaGUgZGlzdHJpYnV0aW9uIG9mIFAtdmFsdWVzIGxvb2tzIHNlbnNpYmxlIGFuZCBmcm9tIHRoZSB2b2xjYW5vIHBsb3Qgd2UgY2FuIHNlZSB0aGF0IHdlIGhhdmUgaWRlbnRpZmllZCBzb21lIERBIG5laWdoYm91cmhvb2RzIGF0IDEwJSBGRFIuCgpXZSBjYW4gdmlzdWFsaXplIERBIG5laWdoYm91cmhvb2RzIGJ1aWxkaW5nIGFuIGFic3RyYWN0ZWQgZ3JhcGguICAKCioqTk9URTogVGhpcyB0YWtlcyBhIHdoaWxlIHRvIHByb2Nlc3MhIENhbiB0YWtlID4zMCBtaW4uKioKCmBgYHtyLCBmaWcud2lkdGg9MTQsIGZpZy5oZWlnaHQ9MTB9CmxpdmVyX21pbG8gPC0gYnVpbGROaG9vZEdyYXBoKGxpdmVyX21pbG8pCnBsb3ROaG9vZEdyYXBoREEobGl2ZXJfbWlsbywgbWlsb19yZXMsIGFscGhhID0gMC4xLCBzaXplX3JhbmdlPWMoMiw2KSkKYGBgCgojIyMgTm90ZTogVGhpcyBzdGVwIGlzIG5vdCByZXF1aXJlZC4gSXQgZ2VuZXJhdGVzIGEgbGFyZ2UgZmlsZSAoZXZlbiBsYXJnZXIgdGhhbiByYXcgZGF0YSkgYW5kIHRoZW4gcmVsb2FkcyBpdC4gCmBgYHtyIHNhdmUgaW50ZXJtZWQgZmlsZXMgMiwgZXZhbD1GQUxTRSwgaW5jbHVkZT1UUlVFLCBlY2hvPVRSVUV9CiMjIFNhdmUgbWlsbyBvYmplY3QgYW5kIHJlc3VsdHMKIyBzYXZlUkRTKGxpdmVyX21pbG8sIn4vZHJvcGJveC12dS90ZW1wL21pbG9fZGF0YS9SYW1hY2hhbmRyYW4yMDE5X2xpdmVyL2xpdmVyX21pbG9fMjAyMTAyMjUuUkRTIikKIyB3cml0ZV9jc3YobWlsb19yZXMsIn4vZHJvcGJveC12dS90ZW1wL21pbG9fZGF0YS9SYW1hY2hhbmRyYW4yMDE5X2xpdmVyL2xpdmVyX3Jlc3VsdHNfMjAyMTAyMjUuY3N2IikKCmBgYAoKYGBge3IgbG9hZCBpbnRlcm1lZCBmaWxlcyAyLCBldmFsPUZBTFNFLCBpbmNsdWRlPVRSVUUsIGVjaG89VFJVRX0KIyBsaXZlcl9taWxvIDwtIHJlYWRSRFMoIn4vbGl2ZXJfbWlsb18yMDIwMTAwOC5SRFMiKQpsaXZlcl9taWxvIDwtIHJlYWRSRFModXJsKCJodHRwczovL3d3dy5kcm9wYm94LmNvbS9zL3hkcDA3Nzg5YzVob2VuMy9saXZlcl9taWxvXzIwMjEwMjI1LlJEUz9kbD0xIikpCiMgbWlsb19yZXMgPC0gcmVhZF9jc3YoIi9uZnMvdGVhbTIwNS9lZDYvZGF0YS9SYW1hY2hhbmRyYW4yMDE5X2xpdmVyL2xpdmVyX3Jlc3VsdHNfMjAyMDEwMDguY3N2IikKbWlsb19yZXMgPC0gcmVhZF9jc3YodXJsKCJodHRwczovL3d3dy5kcm9wYm94LmNvbS9zL2kxbDNhZXAxcHk1d2lyZi9saXZlcl9yZXN1bHRzXzIwMjEwMjI1LmNzdj9kbD0xIikpCmBgYAoKCiMjIyBNYWtpbmcgZmlndXJlcyBmb3IgdGhlIG1hbnVzY3JpcHQKCmBgYHtyLCBmaWcud2lkdGg9MTUsIGZpZy5oZWlnaHQ9MTB9Cgpjb2xvdXJDb3VudCA9IGxlbmd0aCh1bmlxdWUobGl2ZXJfbWlsbyRhbm5vdGF0aW9uX2xpbmVhZ2UpKQpnZXRQYWxldHRlID0gY29sb3JSYW1wUGFsZXR0ZShicmV3ZXIucGFsKDksICJTZXQyIikpCgp1bWFwX2RmIDwtIGRhdGEuZnJhbWUocmVkdWNlZERpbShsaXZlcl9taWxvLCAiVU1BUCIpKQpjb2xuYW1lcyh1bWFwX2RmKSA8LSBjKCJVTUFQXzEiLCAiVU1BUF8yIikKCnVtYXAxIDwtIGNiaW5kKHVtYXBfZGYsIGFubm90YXRpb25fbGluZWFnZT1saXZlcl9taWxvJGFubm90YXRpb25fbGluZWFnZSkgJT4lCiAgZ2dwbG90KGFlcyhVTUFQXzEsIFVNQVBfMiwgY29sb3I9YXMuY2hhcmFjdGVyKGFubm90YXRpb25fbGluZWFnZSkpKSArCiAgZ2VvbV9wb2ludF9yYXN0KHNpemU9MC4xLCBhbHBoYT0wLjUsIHJhc3Rlci5kcGkgPSA4MDApICsKICBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwoZGF0YSA9IC4gJT4lCiAgICAgICAgICAgICAgZ3JvdXBfYnkoYW5ub3RhdGlvbl9saW5lYWdlKSAlPiUKICAgICAgICAgICAgICBzdW1tYXJpc2UoVU1BUF8xPW1lYW4oVU1BUF8xKSwgVU1BUF8yPW1lYW4oVU1BUF8yKSksCiAgICAgICAgICAgIGFlcyhsYWJlbD1hbm5vdGF0aW9uX2xpbmVhZ2UpLCBjb2xvcj0iYmxhY2siLCBzaXplPTYKICAgICAgICAgICAgKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1nZXRQYWxldHRlKGNvbG91ckNvdW50KSkgKwogIGd1aWRlcyhjb2xvcj0ibm9uZSIpICsKICB4bGFiKCJVTUFQMSIpICsgeWxhYigiVU1BUDIiKSArCiAgY29vcmRfZml4ZWQoKSArCiAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAyMikgKwogIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSkKCmZuX2xpdmVyX3VtYXAxIDwtIGZpbGUucGF0aChPVVRQVVRfRElSLCIvbGl2ZXJfdjIvbGl2ZXJfdW1hcDFfbmV3LnBkZiIpCgppZihPVkVSV1JJVEUgfCAhZmlsZS5leGlzdHMoZm5fbGl2ZXJfdW1hcDEpKSB7CiAgZ2dzYXZlKGZuX2xpdmVyX3VtYXAxLCBoZWlnaHQgPSA3LCB3aWR0aCA9IDgpCn0gZWxzZSB7CiAgbWVzc2FnZShjYXQoZm5fbGl2ZXJfdW1hcDEsImV4aXN0cyBhbmQgd2lsbCBub3QgYmUgb3ZlcndyaXR0ZW5cbiIpKQp9CgoKdW1hcDIgPC0KICBjYmluZCh1bWFwX2RmLCBjb25kaXRpb249YXMuY2hhcmFjdGVyKGxpdmVyX21pbG8kY29uZGl0aW9uKSkgJT4lCiAgZ2dwbG90KGFlcyhVTUFQXzEsIFVNQVBfMiwgY29sb3I9Y29uZGl0aW9uKSkgKwogIGdlb21fcG9pbnRfcmFzdChzaXplPTAuMSwgYWxwaGE9MC41LCByYXN0ZXIuZHBpID0gODAwKSArCiAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGU9IlNldDEiLCBuYW1lPScnKSArCiAgeGxhYigiVU1BUDEiKSArIHlsYWIoIlVNQVAyIikgKwogIGNvb3JkX2ZpeGVkKCkgKwogIGd1aWRlcyhjb2xvcj0nbm9uZScpICsKICBmYWNldF93cmFwKGNvbmRpdGlvbn4uLCBuY29sPTEpICsKICB0aGVtZV9ub3RoaW5nKGZvbnRfc2l6ZSA9IDIyKSArCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLCBsZWdlbmQucG9zaXRpb249YygwLjksMC45KSwKICAgICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGNvbG9yPU5BKSwgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTIyKSkKCgpmbl9saXZlcl91bWFwMiA8LSBmaWxlLnBhdGgoT1VUUFVUX0RJUiwiL2xpdmVyX3YyL2xpdmVyX3VtYXAyX25ldy5wZGYiKQoKaWYoT1ZFUldSSVRFIHwgIWZpbGUuZXhpc3RzKGZuX2xpdmVyX3VtYXAxKSkgewogIGdnc2F2ZShmbl9saXZlcl91bWFwMiwgaGVpZ2h0ID0gNywgd2lkdGggPSA4KQp9IGVsc2UgewogIG1lc3NhZ2UoY2F0KGZuX2xpdmVyX3VtYXAyLCJleGlzdHMgYW5kIHdpbGwgbm90IGJlIG92ZXJ3cml0dGVuXG4iKSkKfQoKbmhfZ3JhcGhfcGwgPC0gcGxvdE5ob29kR3JhcGhEQShsaXZlcl9taWxvLCBtaWxvX3JlcywgYWxwaGEgPSAwLjEsIHNpemVfcmFuZ2U9YygxLDQpKSArCiAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCksIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTIyKSkgKwogIGNvb3JkX2ZpeGVkKCkKCmZuX25oX2dyYXBoIDwtIGZpbGUucGF0aChPVVRQVVRfRElSLCIvbGl2ZXJfdjIvbGl2ZXJfZ3JhcGhfbmV3LnBkZiIpCgppZihPVkVSV1JJVEUgfCAhZmlsZS5leGlzdHMoZm5fbmhfZ3JhcGgpKSB7CiAgZ2dzYXZlKGZuX25oX2dyYXBoLCBoZWlnaHQgPSA3LCB3aWR0aCA9IDgpCn0gZWxzZSB7CiAgbWVzc2FnZShjYXQoZm5fbmhfZ3JhcGgsImV4aXN0cyBhbmQgd2lsbCBub3QgYmUgb3ZlcndyaXR0ZW5cbiIpKQp9CgpgYGAKCgpgYGB7ciAsIGZpZy53aWR0aD0xNSwgZmlnLmhlaWdodD0xMH0KZmlnNF90b3AgPC0gKHVtYXAxIHwgdW1hcDIgfCBuaF9ncmFwaF9wbCkgKwogIHBsb3RfbGF5b3V0KHdpZHRocyA9IGMoMywxLDMpKQoKZmlnNF90b3AKYGBgCgojIyMgRXhwbG9yZSBEQSBuZWlnaGJvdXJob29kcyBieSBjZWxsIHR5cGUKCk5leHQsIHdlIGNhbiBjaGVjayB0aGUgY2VsbCB0eXBlcyB3aGVyZSB3ZSBvYnNlcnZlIG1vc3QgZGlmZmVyZW5jZXMgYmV0d2VlbiBoZWFsdGh5IGFuZCBjaXJyaG90aWMgY2VsbHMsIGJ5IHRha2luZyB0aGUgbW9zdCBmcmVxdWVudCBjZWxsIHR5cGUgaW4gZWFjaCBuZWlnaGJvdXJob29kLgoKYGBge3IsIGZpZy53aWR0aD05LCBmaWcuaGVpZ2h0PTEwfQptaWxvX3JlcyA8LSBtaWxvX3Jlc1ssIXN0cl9kZXRlY3QoY29sbmFtZXMobWlsb19yZXMpLCAiYW5ub3RhdGlvbl9saW5lYWdlIildCgojIEFkZCBhbm5vdGF0aW9uIG9mIG1vc3QgZnJlcXVlbnQgY2VsbCB0eXBlIHBlciBuaG9vZCB0byBtaWxvIHJlc3VsdHMgdGFibGUKbWlsb19yZXMgPC0gYW5ub3RhdGVOaG9vZHMobGl2ZXJfbWlsbywgbWlsb19yZXMsICJhbm5vdGF0aW9uX2luZGVwdGgiKQphbm5vX2RmIDwtIGRhdGEuZnJhbWUobGl2ZXJfbWlsb0Bjb2xEYXRhKSAlPiUKICBkaXN0aW5jdChhbm5vdGF0aW9uX2xpbmVhZ2UsIGFubm90YXRpb25faW5kZXB0aCkKbWlsb19yZXMgPC0gbGVmdF9qb2luKG1pbG9fcmVzLCBhbm5vX2RmLCBieT0iYW5ub3RhdGlvbl9pbmRlcHRoIikKYGBgCgpXZSBmaXJzdCBjaGVjayB0aGF0IG5laWdoYm91cmhvb2RzIGFyZSBzdWZmaWNpZW50bHkgaG9tb2dlbmVvdXMKCmBgYHtyfQpmcmFjX2hpc3QgPC0gZ2dwbG90KG1pbG9fcmVzLCBhZXMoYW5ub3RhdGlvbl9pbmRlcHRoX2ZyYWN0aW9uKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbnM9MzApICsKICB4bGFiKCJGcmFjdGlvbiBvZiBjZWxscyBpbiBcbm1vc3QgYWJ1bmRhbnQgY2x1c3RlciIpICsKICB5bGFiKCIjIG5laWdoYm91cmhvb2RzIikgKwogIHRoZW1lX2J3KGJhc2Vfc2l6ZT0xNCkKCmZyYWNfaGlzdApgYGAKCkZpbHRlciBuaG9vZHMgd2l0aCBob21vZ2VuZW91cyBjb21wb3NpdGlvbgoKYGBge3J9Cm1pbG9fcmVzJGFubm90YXRpb25faW5kZXB0aFttaWxvX3JlcyRhbm5vdGF0aW9uX2luZGVwdGhfZnJhY3Rpb24gPCAwLjZdIDwtIE5BCm1pbG9fcmVzJGFubm90YXRpb25fbGluZWFnZVttaWxvX3JlcyRhbm5vdGF0aW9uX2luZGVwdGhfZnJhY3Rpb24gPCAwLjZdIDwtIE5BCmBgYAoKCldlIGNhbiByZWNvdmVyIGFsbCB0aGUgY2x1c3RlcnMgd2hlcmUgREEgd2FzIGRldGVjdGVkIGluIHRoZSBvcmlnaW5hbCBwYXBlcgoKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0xMCwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KZ3JvdXAuYnkgPSAiYW5ub3RhdGlvbl9pbmRlcHRoIgpwYXBlcl9EQSA8LSBsaXN0KGNpcnJob3RpYz1jKCJNUHMgKDQpIiwiTVBzICg1KSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkVuZG90aGVsaWEgKDYpIiwgIkVuZG90aGVsaWEgKDcpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTWVzICgzKSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRjZWxscyAoMikiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNeW9maWJyb2JsYXN0cyIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgIGhlYWx0aHk9YygiTVBzICg3KSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJFbmRvdGhlbGlhICgxKSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJUY2VsbHMgKDEpIiwgIlRjZWxscyAoMykiLCJUY2VsbHMgKDEpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIklMQ3MgKDEpIgogICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgKQoKZXhwREFfZGYgPC0gYmluZF9yb3dzKAogIGRhdGEuZnJhbWUoYW5ub3RhdGlvbl9pbmRlcHRoID0gcGFwZXJfREFbWyJjaXJyaG90aWMiXV0sIHByZWRfREE9ImNpcnJob3RpYyIpLAogIGRhdGEuZnJhbWUoYW5ub3RhdGlvbl9pbmRlcHRoID0gcGFwZXJfREFbWyJoZWFsdGh5Il1dLCBwcmVkX0RBPSJoZWFsdGh5IikKICApCgpwbDEgPC0gbWlsb19yZXMgJT4lCiAgbGVmdF9qb2luKGV4cERBX2RmKSAlPiUKICBtdXRhdGUoaXNfc2lnbmlmID0gaWZlbHNlKFNwYXRpYWxGRFIgPCAwLjEsIDEsIDApKSAlPiUKICBtdXRhdGUobG9nRkNfY29sb3IgPSBpZmVsc2UoaXNfc2lnbmlmPT0xLCBsb2dGQywgTkEpKSAlPiUKICBhcnJhbmdlKGFubm90YXRpb25fbGluZWFnZSkgJT4lCiAgbXV0YXRlKE5ob29kPWZhY3RvcihOaG9vZCwgbGV2ZWxzPXVuaXF1ZShOaG9vZCkpKSAlPiUKICBmaWx0ZXIoIWlzLm5hKGFubm90YXRpb25fbGluZWFnZSkpICU+JQogIGdncGxvdChhZXMoYW5ub3RhdGlvbl9pbmRlcHRoLCBsb2dGQywgY29sb3I9bG9nRkNfY29sb3IpKSArCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQyKCkgKwogIGd1aWRlcyhjb2xvcj0ibm9uZSIpICsKICB4bGFiKGdyb3VwLmJ5KSArIHlsYWIoIkxvZyBGb2xkIENoYW5nZSIpICsKICBnZ2JlZXN3YXJtOjpnZW9tX3F1YXNpcmFuZG9tKGFscGhhPTEpICsKICBjb29yZF9mbGlwKCkgKwogIGZhY2V0X2dyaWQoYW5ub3RhdGlvbl9saW5lYWdlfi4sIHNjYWxlcz0iZnJlZSIsIHNwYWNlPSJmcmVlIikgKwogIHRoZW1lX2J3KGJhc2Vfc2l6ZT0yMikgKwogIHRoZW1lKHN0cmlwLnRleHQueSA9ICBlbGVtZW50X3RleHQoYW5nbGU9MCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICkKCnBsMiA8LSBtaWxvX3JlcyAlPiUKICBsZWZ0X2pvaW4oZXhwREFfZGYpICU+JQogICMgZHBseXI6OmZpbHRlcighaXMubmEocHJlZF9EQSkpICU+JQogIGdyb3VwX2J5KGFubm90YXRpb25faW5kZXB0aCkgJT4lCiAgc3VtbWFyaXNlKHByZWRfREE9ZHBseXI6OmZpcnN0KHByZWRfREEpLCBhbm5vdGF0aW9uX2xpbmVhZ2U9ZHBseXI6OmZpcnN0KGFubm90YXRpb25fbGluZWFnZSkpICU+JQogIG11dGF0ZShlbmQ9aWZlbHNlKHByZWRfREE9PSJoZWFsdGh5IiwgMCwgMSksCiAgICAgICAgIHN0YXJ0PWlmZWxzZShwcmVkX0RBPT0iaGVhbHRoeSIsIDEsIDApKSAlPiUKICBmaWx0ZXIoIWlzLm5hKGFubm90YXRpb25fbGluZWFnZSkpICU+JQogIGdncGxvdChhZXMoYW5ub3RhdGlvbl9pbmRlcHRoLCBzdGFydCwgeGVuZCA9IGFubm90YXRpb25faW5kZXB0aCwgeWVuZCA9IGVuZCwgY29sb3I9cHJlZF9EQSkpICsKICBnZW9tX3NlZ21lbnQoc2l6ZT0xLGFycm93PWFycm93KGxlbmd0aCA9IHVuaXQoMC4xLCAibnBjIiksIHR5cGU9ImNsb3NlZCIpKSArCiAgY29vcmRfZmxpcCgpICsKICB4bGFiKCJhbm5vdGF0aW9uIikgKwogIGZhY2V0X2dyaWQoYW5ub3RhdGlvbl9saW5lYWdlfi4sCiAgICAjIGFubm90YXRpb25fbGluZWFnZX4iUmFtYWNoYW5kcmFuIGV0IGFsLlxuREEgcHJlZGljdGlvbnMiLAogICAgICAgICAgICAgc2NhbGVzPSJmcmVlIiwgc3BhY2U9ImZyZWUiKSArCiAgIyBndWlkZXMoY29sb3I9Im5vbmUiKSArCiAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGU9IlNldDEiLCBkaXJlY3Rpb24gPSAtMSwKICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoImVucmljaGVkIGluIGNpcnJob3RpYyIsICJlbnJpY2hlZCBpbiBoZWFsdGh5IiksCiAgICAgICAgICAgICAgICAgICAgIG5hLnRyYW5zbGF0ZSA9IEYsCiAgICAgICAgICAgICAgICAgICAgIG5hbWU9IlJhbWFjaGFuZHJhbiBldCBhbC5cbkRBIHByZWRpY3Rpb25zIikgKwogIGd1aWRlcyhjb2xvcj1ndWlkZV9sZWdlbmQobmNvbCA9IDEpKSArCiAgdGhlbWVfYncoYmFzZV9zaXplPTIyKSArCiAgeWxpbSgtMC4xLDEuMSkgKwogIHRoZW1lKHN0cmlwLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSxzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9OTApLAogICAgICAgIHBsb3QubWFyZ2luID0gdW5pdChjKDAsMCwwLDApLCAiY20iKSwgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCgpmaWc0X2JsZWZ0IDwtIChwbDIgKyBwbDEgKwogIHBsb3RfbGF5b3V0KHdpZHRocz1jKDEsMTApLCBndWlkZXMgPSAiY29sbGVjdCIpICYgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ3RvcCcsIGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gMCkpCgpmbl9saXZlcl9EQWNvbXAgPC0gZmlsZS5wYXRoKE9VVFBVVF9ESVIsIi9saXZlcl92Mi9saXZlcl9EQWNvbXBhcmlzb24ucGRmIikKCmlmKE9WRVJXUklURSB8ICFmaWxlLmV4aXN0cyhmbl9saXZlcl9EQWNvbXApKSB7CiAgZ2dzYXZlKGZuX2xpdmVyX0RBY29tcCwgaGVpZ2h0ID0gMTMsIHdpZHRoID0gOCkKfSBlbHNlIHsKICBtZXNzYWdlKGNhdChmbl9saXZlcl9EQWNvbXAsImV4aXN0cyBhbmQgd2lsbCBub3QgYmUgb3ZlcndyaXR0ZW5cbiIpKQp9CiMgZ2dzYXZlKCJ+L21vdW50L2dkcml2ZS9taWxvL0ZpZ3VyZXMvbGl2ZXJfdjIvbGl2ZXJfREFjb21wYXJpc29uLnBkZiIsIHdpZHRoPTgsIGhlaWdodCA9IDEzKQojIGdnc2F2ZSgifi9kcm9wYm94LXZ1L3RlbXAvbWlsb19vdXRwdXQvbGl2ZXJfdjIvbGl2ZXJfREFjb21wYXJpc29uLnBkZiIsIHdpZHRoPTgsIGhlaWdodCA9IDEzKQpgYGAKCmBgYHtyIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTEzfQpmaWc0X2JsZWZ0CmBgYAoKCiMjIyBDbG9zZS11cCBvbiBFbmRvdGhlbGlhbCBsaW5lYWdlCgpgYGB7cn0KZW5kb19taWxvIDwtIHNjYXRlcjo6cnVuVU1BUChsaXZlcl9taWxvWyxsaXZlcl9taWxvJGFubm90YXRpb25fbGluZWFnZT09IkVuZG90aGVsaWEiXSwgIGRpbXJlZD0nUENBJykKcGxvdFVNQVAoZW5kb19taWxvLCBjb2xvdXJfYnkgPSAiYW5ub3RhdGlvbl9pbmRlcHRoIikKYGBgCgpgYGB7cn0KdW1hcF9kZiA8LSBkYXRhLmZyYW1lKHJlZHVjZWREaW0oZW5kb19taWxvLCAiVU1BUCIpKQpjb2xuYW1lcyh1bWFwX2RmKSA8LSBjKCJVTUFQXzEiLCAiVU1BUF8yIikKCmVuZG9fdW1hcCA8LSBjYmluZCh1bWFwX2RmLCBjb25kaXRpb249ZW5kb19taWxvJGNvbmRpdGlvbikgJT4lCiAgIGdncGxvdChhZXMoVU1BUF8xLCBVTUFQXzIsIGNvbG9yPWNvbmRpdGlvbikpICsKICBnZW9tX3BvaW50KHNpemU9MC4zLCBhbHBoYT0wLjUpICsKICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZT0iU2V0MSIsIG5hbWU9JycpICsKICB4bGFiKCJVTUFQMSIpICsgeWxhYigiVU1BUDIiKSArCiAgY29vcmRfZml4ZWQoKSArCiAgZ3VpZGVzKGNvbG9yPSJub25lIikgKwogIGZhY2V0X3dyYXAoY29uZGl0aW9ufi4sIG5jb2w9MSkgKwogIHRoZW1lX25vdGhpbmcoKSArCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLCBsZWdlbmQucG9zaXRpb249YygwLjksMC45KSwKICAgICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGNvbG9yPU5BKSwgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTIyKSkKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9NCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGl2ZXJfbWlsbzIgPC0gbGl2ZXJfbWlsbwpzdWJzZXQubmhvb2RzIDwtIHN0cl9kZXRlY3QobWlsb19yZXMkYW5ub3RhdGlvbl9pbmRlcHRoLCAiRW5kbyIpCnJlZHVjZWREaW0obGl2ZXJfbWlsbzIsICJVTUFQIilbY29sbmFtZXMoZW5kb19taWxvKSxdIDwtIHJlZHVjZWREaW0oZW5kb19taWxvLCAiVU1BUCIpIAoKZW5kb19nciA8LQogIHBsb3ROaG9vZEdyYXBoREEoCiAgbGl2ZXJfbWlsbzIsIG1pbG9fcmVzLAogIHN1YnNldC5uaG9vZHMgPSB3aGljaChtaWxvX3JlcyRhbm5vdGF0aW9uX2xpbmVhZ2UgPT0gIkVuZG90aGVsaWEiKSwgCiAgc2l6ZV9yYW5nZT1jKDEsNCksCiAgIyApID0pWzE6KGxlbmd0aCgpLTEpXSwgCiAgYWxwaGEgPSAwLjEKICApICArCiAgIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MjApLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0yMikpCiAgCmZpZzRfYnJpZ2h0MSA8LSAoKGVuZG9fdW1hcCArIGVuZG9fZ3IgKSArIAogIHBsb3RfbGF5b3V0KHdpZHRocyA9IGMoMSwyKSwgCiAgICAgICAgICAgICAgICBndWlkZXMgPSAiY29sbGVjdCIKICAgICAgICAgICAgICAgICkpIApmaWc0X2JyaWdodDEKYGBgCgoKIyMjIENhbid0IGdldCBjb2RlIGJlbG93IHRvIHJ1bgpVbmxjZWFyIHdoYXQgdGhlIHByb2JsZW0gaXM7IGRhdGEgc3RydWN0dXJlPwpgYGB7cn0KIyAjIG5oX2dyYXBoIDwtIG5ob29kR3JhcGgobGl2ZXJfbWlsbylbc3Vic2V0Lm5ob29kcyxzdWJzZXQubmhvb2RzXQojIAojIG5oX2dyYXBoIDwtIG5ob29kR3JhcGgobGl2ZXJfbWlsbylbbmEub21pdChzdWJzZXQubmhvb2RzKV0KIyAKIyBuaF9ncmFwaCA8LSBncmFwaF9mcm9tX2FkamFjZW5jeV9tYXRyaXgobmhfZ3JhcGgpCiMgCiMgY29sX3ZhbHMgPC0gY29sRGF0YShsaXZlcl9taWxvKVthcy5udW1lcmljKHZlcnRleF9hdHRyKG5oX2dyYXBoKSRuYW1lKSwgY29sb3VyX2J5XQojIFYobmhfZ3JhcGgpJGNvbG91cl9ieSA8LSBpZmVsc2UobWlsb19yZXNbc3Vic2V0Lm5ob29kcywiU3BhdGlhbEZEUiJdID4gMC4xLCAwLCBtaWxvX3Jlc1tzdWJzZXQubmhvb2RzLCJsb2dGQyJdKQojIGdncmFwaChzaW1wbGlmeShuaF9ncmFwaCkpICsKIyAgICAgICBnZW9tX2VkZ2VfbGluazAoZWRnZV9jb2xvdXIgPSAiZ3JleTY2IiwgZWRnZV9hbHBoYT0wLjIpICAgKwojICAgICAgIGdlb21fbm9kZV9wb2ludChhZXMoZmlsbCA9IGNvbG91cl9ieSksIHNoYXBlPTIxLCBzaXplPTIpICsKIyAgIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKCkKYGBgCgoKIyMjIENsb3NlLXVwIG9uIENob2xhbmdpb2N5dGVzCgpgYGB7cn0KY2hvbF9taWxvIDwtIHNjYXRlcjo6cnVuVU1BUChsaXZlcl9taWxvWyxsaXZlcl9taWxvJGFubm90YXRpb25fbGluZWFnZT09IkNob2xhbmdpb2N5dGVzIl0sICBkaW1yZWQ9J1BDQScpCnBsb3RVTUFQKGNob2xfbWlsbywgY29sb3VyX2J5ID0gImFubm90YXRpb25faW5kZXB0aCIpCgpwbG90VU1BUChjaG9sX21pbG8sIGNvbG91cl9ieSA9ICJwZXJjZW50Lm1pdG8iKQpgYGAKCkZpbHRlciBvdXQgY2VsbHMgdGhhdCBzaG93IGNvbnRhbWluYXRpb24gZnJvbSBpbW11bmUgY2VsbHMgKGV4cHJlc3Npb24gb2YgaW1tdW5lIG1hcmtlcnMpCgpgYGB7cn0Ka2VlcCA8LSBsb2djb3VudHMoY2hvbF9taWxvKVsiQ0QxOSIsXSA9PSAwIHwgbG9nY291bnRzKGNob2xfbWlsbylbIk1TNEExIixdID09IDAKY2hvbF9taWxvIDwtIGNob2xfbWlsb1ssa2VlcF0KY2hvbF9taWxvIDwtIHNjYXRlcjo6cnVuVU1BUChjaG9sX21pbG8sICBkaW1yZWQ9J1BDQScpCgpwbG90VU1BUChjaG9sX21pbG8sIGNvbG91cl9ieSA9ICJhbm5vdGF0aW9uX2luZGVwdGgiKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9OH0KdW1hcF9kZiA8LSBkYXRhLmZyYW1lKHJlZHVjZWREaW0oY2hvbF9taWxvLCAiVU1BUCIpKQpjb2xuYW1lcyh1bWFwX2RmKSA8LSBjKCJVTUFQXzEiLCAiVU1BUF8yIikKCmNob2xfdW1hcCA8LSBjYmluZCh1bWFwX2RmLCBjb25kaXRpb249Y2hvbF9taWxvJGNvbmRpdGlvbikgJT4lCiAgIGdncGxvdChhZXMoVU1BUF8xLCBVTUFQXzIsIGNvbG9yPWNvbmRpdGlvbikpICsKICBnZW9tX3BvaW50KHNpemU9MC4zLCBhbHBoYT0wLjUpICsKICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZT0iU2V0MSIsIG5hbWU9JycpICsKICB4bGFiKCJVTUFQMSIpICsgeWxhYigiVU1BUDIiKSArCiAgY29vcmRfZml4ZWQoKSArCiAgZ3VpZGVzKGNvbG9yPSJub25lIikgKwogIGZhY2V0X3dyYXAoY29uZGl0aW9ufi4sIG5jb2w9MSkgKwogIHRoZW1lX25vdGhpbmcoKSArCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLCBsZWdlbmQucG9zaXRpb249YygwLjksMC45KSwKICAgICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGNvbG9yPU5BKSwgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTIyKSkKCmNob2xfdW1hcApgYGAKCmBgYHtyLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD00LCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpsaXZlcl9taWxvMiA8LSBsaXZlcl9taWxvCnN1YnNldC5uaG9vZHMgPC0gbWlsb19yZXMkYW5ub3RhdGlvbl9saW5lYWdlPT0iQ2hvbGFuZ2lvY3l0ZXMiCnJlZHVjZWREaW0obGl2ZXJfbWlsbzIsICJVTUFQIilbY29sbmFtZXMoY2hvbF9taWxvKSxdIDwtIHJlZHVjZWREaW0oY2hvbF9taWxvLCAiVU1BUCIpIAoKY2hvbF9nciA8LQogIHBsb3ROaG9vZEdyYXBoREEoCiAgbGl2ZXJfbWlsbzIsIG1pbG9fcmVzLAogIHN1YnNldC5uaG9vZHMgPSBzdWJzZXQubmhvb2RzLAogIHNpemVfcmFuZ2U9YygyLDUpLAogICMgKSA9KVsxOihsZW5ndGgoKS0xKV0sIAogIGFscGhhID0gMC4xCiAgKSAgKwogICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTIyKSwgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MjQpKQogIAooY2hvbF91bWFwICsgY2hvbF9nciApICsgCiAgcGxvdF9sYXlvdXQod2lkdGhzID0gYygxLDIpLCAKICAgICAgICAgICAgICAgIGd1aWRlcyA9ICJjb2xsZWN0IgogICAgICAgICAgICAgICAgKQojIGZpZzRfYnJpZ2h0MSArCiMgICBnZ3NhdmUoIn4vbWlsb19vdXRwdXQvbGl2ZXJfZW5kb0dyYXBoLnBkZiIsIHdpZHRoPTksIGhlaWdodCA9IDUpICAKCiMgZ2dzYXZlKCJ+L2Ryb3Bib3gtdnUvdGVtcC9taWxvX291dHB1dC9saXZlcl9lbmRvR3JhcGgucGRmIiwgd2lkdGg9OSwgaGVpZ2h0ID0gNSkgIApgYGAKCiMjIyBEaWZmZXJlbnRpYWwgR2VuZSBFeHByZXNzaW9uIGFuYWx5c2lzCgpJbiBhIHN1YnNldCBvZiBsaW5lYWdlcywgd2Ugd2FudCB0byB0ZXN0IGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBiZXR3ZWVuIG5laWdoYm91cmhvb2RzIGVucmljaGVkIGluIGNpcnJob3RpYyBjZWxscyBhbmQgbmVpZ2hib3VyaG9vZHMgZW5yaWNoZWQuICAKCkFkZCBuaG9vZCBleHByZXNzaW9uIHRvIHNwZWVkLXVwIHBsb3R0aW5nIG9mIGhlYXRtYXBzCgpgYGB7cn0KbGl2ZXJfbWlsbyA8LSBjYWxjTmhvb2RFeHByZXNzaW9uKGxpdmVyX21pbG8sIGFzc2F5ID0gImxvZ2NvdW50cyIsIHN1YnNldC5yb3cgPSBodmdzKQpgYGAKCi0tLS0tCiMgRFJUOiBzdG9wcGluZyBydW4tdGhyb3VnaCBoZXJlClNob3VsZCBiZSBhYmxlIHRvIHJ1biBldmVyeXRoaW5nIGFib3ZlLgotLS0tLQoKIyMgRW5kb3RoZWxpYQoKUmVidXR0YWwgZmlndXJlIHNob3djYXNpbmcgZ3JvdXBpbmcKCmBgYHtyLCBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD0xNH0Kc2V0LnNlZWQoNDIpCm1pbG9fcmVzX2VuZG9ncm91cHMgPC0gZ3JvdXBOaG9vZHMobGl2ZXJfbWlsbywgbWlsb19yZXMsIG1heC5sZmMuZGVsdGEgPSAyLCBvdmVybGFwID0gMSkKCnAxIDwtIHBsb3ROaG9vZEdyb3VwcyhsaXZlcl9taWxvLCBtaWxvX3Jlc19lbmRvZ3JvdXBzLCAKICAgICAgICAgICAgICAgIHNpemVfcmFuZ2U9YygxLDMpKSAKCm1pbG9fcmVzX2VuZG9ncm91cHMgPC0gYW5ub3RhdGVOaG9vZHMobGl2ZXJfbWlsbywgbWlsb19yZXNfZW5kb2dyb3VwcywgJ2Fubm90YXRpb25fbGluZWFnZScpCgpwMiA8LSBwbG90REFiZWVzd2FybShtaWxvX3Jlc19lbmRvZ3JvdXBzLCBncm91cC5ieSA9ICdOaG9vZEdyb3VwJykgKwogIGZhY2V0X2dyaWQoYW5ub3RhdGlvbl9saW5lYWdlfi4sIHNjYWxlcz0iZnJlZSIsIHNwYWNlPSJmcmVlIikKCgojIyBQbG90IGV4cHJlc3Npb24gaW4gVCBjZWxsIG5laWdoYm91cmhvb2RzCiMgbWFya2Vyc19kZiA8LSByZWFkX2Nzdigifi9tb3VudC9nZHJpdmUvbWlsby9TVGFibGUzX1JhbWFjaGFuZHJhbi5jc3YiKQp0Y2VsbF9tYXJrZXJfZ2VuZXMgPC0gCiAgbWFya2Vyc19kZiAlPiUKICBmaWx0ZXIoY2x1c3RlciAlaW4lIGMoIlRjZWxsIiwgIklMQyIpKSAlPiUKICB0b3BfbigzMCwgbXlBVUMpICU+JQogIHB1bGwoZ2VuZSkKCnAzIDwtIHBsb3ROaG9vZEV4cHJlc3Npb25Hcm91cHMobGl2ZXJfbWlsbywgbWlsb19yZXNfZW5kb2dyb3VwcywgZmVhdHVyZXMgPSB1bmlxdWUodGNlbGxfbWFya2VyX2dlbmVzKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgc3Vic2V0Lm5ob29kcyA9IG1pbG9fcmVzX2VuZG9ncm91cHMkTmhvb2RHcm91cCAlaW4lIGMoIjMiLCIxMCIsICIxNCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgIHNjYWxlPVRSVUUsIGNsdXN0ZXJfZmVhdHVyZXMgPSBUUlVFLHNob3dfcm93bmFtZXMgPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgKSArCiAgdGhlbWUoc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTkwKSkKCmBgYApgYGB7ciwgZmlnLndpZHRoPTE1LCBmaWcuaGVpZ2h0PTEwfQooKChwMSArIHRoZW1lKCkpLyAocDMgKyB0aGVtZShzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTAsIGFuZ2xlPTQ1KSkpKSArIAogIHBsb3RfbGF5b3V0KGhlaWdodHMgPSBjKDEuMSwxKSwgZ3VpZGVzPSJjb2xsZWN0IgogICAgICAgICAgICAgICl8IAogICgKICAgIHAyICsgdGhlbWVfYncoYmFzZV9zaXplPTE2KSArIHRoZW1lKHN0cmlwLnRleHQueSA9IGVsZW1lbnRfdGV4dChhbmdsZT0wKSkKICAgICkpICsgCiAgcGxvdF9sYXlvdXQod2lkdGhzID0gYygxLjQsIDEpKSArCiAgcGxvdF9hbm5vdGF0aW9uKHRhZ19sZXZlbHMgPSBjKCJBIiwgIkMiLCAiQiIpICkgKwogICMgZ2dzYXZlKCJ+L21vdW50L2dkcml2ZS9taWxvL0ZpZ3VyZXMvbGl2ZXJfdjIvUkZpZ19ncm91cGluZy5wZGYiLCB3aWR0aD0xNSwgaGVpZ2h0ID0gMTIpICsKICAjIGdnc2F2ZSgifi9tb3VudC9nZHJpdmUvbWlsby9GaWd1cmVzL2xpdmVyX3YyL1JGaWdfZ3JvdXBpbmcucG5nIiwgd2lkdGg9MTUsIGhlaWdodCA9IDEyKQpgYGAKCkdyb3VwIGVuZG90aGVsaWFsIGNlbGxzIGJ5IGxvZ0ZDIGFuZCBEQSByZXN1bHRzCgpgYGB7cn0KbWlsb19yZXNfZW5kb2dyb3VwcyRhbm5vdGF0aW9uX2luZGVwdGhbbWlsb19yZXNfZW5kb2dyb3VwcyRhbm5vdGF0aW9uX2luZGVwdGhfZnJhY3Rpb24gPCAwLjZdIDwtIE5BCm1pbG9fcmVzX2VuZG9ncm91cHMkYW5ub3RhdGlvbl9saW5lYWdlW21pbG9fcmVzX2VuZG9ncm91cHMkYW5ub3RhdGlvbl9pbmRlcHRoX2ZyYWN0aW9uIDwgMC42XSA8LSBOQQoKIyMgR3JvdXAgbmVpZ2hib3VyaG9vZHMgYnkgREEgb3V0Y29tZQptaWxvX3Jlc19lbmRvZ3JvdXBzJE5ob29kR3JvdXAgPC0gTkEKbWlsb19yZXNfZW5kb2dyb3VwcyROaG9vZEdyb3VwIDwtIGlmZWxzZSgobWlsb19yZXNfZW5kb2dyb3VwcyRhbm5vdGF0aW9uX2xpbmVhZ2UgPT0gIkVuZG90aGVsaWEiKSAmIChtaWxvX3Jlc19lbmRvZ3JvdXBzJFNwYXRpYWxGRFIgPCAwLjEpICYgKG1pbG9fcmVzX2VuZG9ncm91cHMkbG9nRkMgPCAtMi41KSwgIjU0IiwgbWlsb19yZXNfZW5kb2dyb3VwcyROaG9vZEdyb3VwKQptaWxvX3Jlc19lbmRvZ3JvdXBzJE5ob29kR3JvdXAgPC0gaWZlbHNlKChtaWxvX3Jlc19lbmRvZ3JvdXBzJGFubm90YXRpb25fbGluZWFnZSA9PSAiRW5kb3RoZWxpYSIpICYgKG1pbG9fcmVzX2VuZG9ncm91cHMkU3BhdGlhbEZEUiA8IDAuMSkgJiAobWlsb19yZXNfZW5kb2dyb3VwcyRsb2dGQyA+IDIuNSksICI3MCIsIG1pbG9fcmVzX2VuZG9ncm91cHMkTmhvb2RHcm91cCkKCgpsaXZlcl9taWxvMiA8LSBsaXZlcl9taWxvCnN1YnNldC5uaG9vZHMgPC0gc3RyX2RldGVjdChtaWxvX3JlcyRhbm5vdGF0aW9uX2luZGVwdGgsICJFbmRvIikKcmVkdWNlZERpbShsaXZlcl9taWxvMiwgIlVNQVAiKVtjb2xuYW1lcyhlbmRvX21pbG8pLF0gPC0gcmVkdWNlZERpbShlbmRvX21pbG8sICJVTUFQIikgCmVuZG9fZ3JfZ3JvdXBzIDwtIHBsb3ROaG9vZEdyb3VwcyhsaXZlcl9taWxvMiwgbWlsb19yZXNfZW5kb2dyb3Vwc1ttaWxvX3Jlc19lbmRvZ3JvdXBzJGFubm90YXRpb25fbGluZWFnZT09IkVuZG90aGVsaWEiLF0sIAogICAgICAgICAgICAgICAgc2hvd19ncm91cHMgPSBjKCI1NCIsICI3MCIpLAogICAgICAgICAgICAgICAgc2l6ZV9yYW5nZT1jKDEsNCksCiAgICAgICAgICAgICAgICBzdWJzZXQubmhvb2RzID0gbWlsb19yZXNfZW5kb2dyb3VwcyRhbm5vdGF0aW9uX2xpbmVhZ2U9PSJFbmRvdGhlbGlhIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCI1NCI9YnJld2VyLnBhbCg0LCAiU3BlY3RyYWwiKVsyXSwgIjcwIj1icmV3ZXIucGFsKDQsICJTcGVjdHJhbCIpWzNdKSwgCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIjU0Ij0iVW5pbmp1cmVkIGdyb3VwIiwgJzcwJz0gIkNpcnJob3RpYyBncm91cCIpLAogICAgICAgICAgICAgICAgICAgIG5hLnZhbHVlPSJ3aGl0ZSIsCiAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJOaG9vZCBncm91cCIKICAgICAgICAgICAgICAgICAgICApICsKICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTIwKSwgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MjIpKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9MTgsIGZpZy5oZWlnaHQ9NX0KZmlnNF9icmlnaHQxIDwtICgoZW5kb191bWFwICsgZW5kb19ncikgKyAKICBwbG90X2xheW91dCh3aWR0aHMgPSBjKDEsMiksIGd1aWRlcz0iY29sbGVjdCIKICAgICAgICAgICAgICAgICkpICYKICB0aGVtZShsZWdlbmQuYm94ID0gImhvcml6b250YWwiLCBsZWdlbmQucG9zaXRpb24gPSAidG9wIiwgbGVnZW5kLmRpcmVjdGlvbiA9ICJ2ZXJ0aWNhbCIpCmZpZzRfYnJpZ2h0MQpgYGAKCgpDYWxjdWxhdGUgbWFya2VyIGdlbmVzIGJldHdlZW4gdGhlIHR3byBncm91cHMKYGBge3J9Cm1pdG9fZ2VuZXMgPC0gc3RyX2RldGVjdChodmdzLCAiXk1ULSIpCm1hcmtlcnNfZGYgPC0gZmluZE5ob29kR3JvdXBNYXJrZXJzKGxpdmVyX21pbG8sIGRhLnJlcyA9IG1pbG9fcmVzX2VuZG9ncm91cHMsIGFzc2F5PSJjb3VudHMiLAogICAgICAgICAgICAgICAgICAgICAgc3Vic2V0Lm5ob29kcyA9IChtaWxvX3Jlc19lbmRvZ3JvdXBzJE5ob29kR3JvdXAgJWluJSBjKCI1NCIsICI3MCIpKSwKICAgICAgICAgICAgICAgICAgICAgIHN1YnNldC5ncm91cHMgPSBjKCI1NCIsICI3MCIpLAogICAgICAgICAgICAgICAgICAgICAgc3Vic2V0LnJvdyA9IGh2Z3NbIW1pdG9fZ2VuZXNdLAogICAgICAgICAgICAgICAgICAgICAgYWdncmVnYXRlLnNhbXBsZXMgPSBUUlVFLCBzYW1wbGVfY29sID0gImRhdGFzZXQiCiAgICAgICAgICAgICAgICAgICAgICApCgptaWxvX3Jlc19lbmRvZ3JvdXBzW21pbG9fcmVzX2VuZG9ncm91cHMkTmhvb2RHcm91cCAlaW4lIGMoIjU0IiwgIjcwIiksXQoKY29sbmFtZXMobWFya2Vyc19kZikgPC0gc3RyX3JlcGxhY2UoY29sbmFtZXMobWFya2Vyc19kZiksICI3MCIsICJjaXJyIikKY29sbmFtZXMobWFya2Vyc19kZikgPC0gc3RyX3JlcGxhY2UoY29sbmFtZXMobWFya2Vyc19kZiksICI1NCIsICJ1bmluaiIpCmBgYAoKIyMjIyBWaXN1YWxpemUgYXMgdm9sY2FubyAKCmBgYHtyLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCmhpZ2hsaWdodF9nZW5lcyA8LSBjKCJQTFZBUCIsICJWV0ExIiwgIkFDS1IxIiwgIklMMzIiLAogICAgICAgICAgICAgICAgICAgICAiQ0xFQzRHIiwgIkNMRUM0TSIsICJGQ04yIiwgIkZDTjMiLAogICAgICAgICAgICAgICAgICAgICAiTEVGMSIpCgptYXJrZXIuZGYgPC0gbWFya2Vyc19kZgptYXJrZXIuZGYgJT4lCiAgbXV0YXRlKGxhYmVsPWlmZWxzZShHZW5lSUQgJWluJSBoaWdobGlnaHRfZ2VuZXMsIEdlbmVJRCwgTkEpKSAlPiUKICBnZ3Bsb3QoYWVzKGxvZ0ZDX2NpcnIsIC1sb2cxMChhZGouUC5WYWxfY2lyciksIAogICAgICAgICAgICAgIyBjb2xvcj1oaWdobGlnaHQKICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsPWxhYmVsKSwgY29sb3I9InJlZCIpICsKICB4bGFiKCJsb2dGQyIpICsgeWxhYigiLSBsb2cxMChBZGouIFAgdmFsdWUpIikgKwogIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDIyKQogIApgYGAKCgojIyMjIFZpc3VhbGl6ZSBhcyBoZWF0bWFwIAooZ2VuZSBleHByZXNzaW9uIHZhbHVlcyBhcmUgc2NhbGVkIGJldHdlZW4gMCBhbmQgMSBmb3IgZWFjaCBnZW5lKQoKYGBge3IsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMiwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbWFya2VyX2dlbmVzIDwtIG1hcmtlci5kZiAlPiUKICBkcGx5cjo6ZmlsdGVyKGFkai5QLlZhbF9jaXJyIDwgMC4wNSkgJT4lCiAgcHVsbChHZW5lSUQpCgpmaWc0X2JicmlnaHQgPC0KICBwbG90Tmhvb2RFeHByZXNzaW9uREEobGl2ZXJfbWlsbywgbWlsb19yZXNfZW5kb2dyb3VwcywgYyhtYXJrZXJfZ2VuZXMpLCBjbHVzdGVyX2ZlYXR1cmVzID0gVFJVRSwgYXNzYXkgPSAiY291bnRzIiwKICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gMC4xLAogICAgICAgICAgICAgICAgICAgICAgc2NhbGVfdG9fMSA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICBzdWJzZXQubmhvb2RzID0gIG1pbG9fcmVzX2VuZG9ncm91cHMkTmhvb2RHcm91cCAlaW4lIGMoIjU0IiwgIjcwIiksCiAgICAgICAgICAgICAgICAgICAgICAjIGdyaWQuc3BhY2UgPSAiZnJlZSIsCiAgICAgICAgICAgICAgICAgICAgICBoaWdobGlnaHRfZmVhdHVyZXMgPSBoaWdobGlnaHRfZ2VuZXMsIHNob3dfcm93bmFtZXMgPSBGQUxTRQogICAgICAgICAgICAgICAgICAgICAgKSArCiAgeWxhYigiREUgZ2VuZXMiKSsKICAjIGZhY2V0X2dyaWQoLn5OaG9vZEdyb3VwLCBzY2FsZXM9ImZyZWUiLCBzcGFjZT0iZnJlZSIpCiAgIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MjIpLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0yNCkpICsKICBwbG90X2xheW91dChoZWlnaHRzID0gYygxLDEwKSkgJiB0aGVtZShsZWdlbmQubWFyZ2luID0gbWFyZ2luKDAsMCwwLDYwKSwgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpCgogIApwbDMgPC0gZmlnNF9iYnJpZ2h0JGRhdGEgJT4lCiAgZ2dwbG90KGFlcyhsb2dGQ19yYW5rLCAxLGZpbGw9bG9nRkMpKSArCiAgZ2VvbV90aWxlKCkgKwogICAgICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZT0xNikgKwogICAgeWxhYigiIikgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKG5hbWU9IkRBIGxvZ0ZDIikgKwogICAgIyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiNTQiPWJyZXdlci5wYWwoNCwgIlNwZWN0cmFsIilbMl0sICI3MCI9YnJld2VyLnBhbCg0LCAiU3BlY3RyYWwiKVszXSksIAogICAgIyAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIjU0Ij0iVW5pbmp1cmVkIGdyb3VwIiwgJzcwJz0gIkNpcnJob3RpYyBncm91cCIpLAogICAgIyAgICAgICAgICAgICAgICAgbmEudmFsdWU9IndoaXRlIiwKICAgICMgICAgICAgICAgICAgICAgIG5hbWUgPSAiTmhvb2QgZ3JvdXAiCiAgICAjICAgICAgICAgICAgICAgICApICsKICAgIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBjKDAuMDEsIDApKSArCiAgICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQoKZmlnNF9iYnJpZ2h0IDwtIHBsMyAvIGZpZzRfYmJyaWdodCAgKwogIHBsb3RfbGF5b3V0KGhlaWdodHMgPSBjKDEsMjApKQoKZmlnNF9iYnJpZ2h0CmBgYAoKIyMjIEdPIHRlcm0gYW5hbHlzaXMKCmBgYHtyLCBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQojIEJpb2NNYW5hZ2VyOjppbnN0YWxsKCdjbHVzdGVyUHJvZmlsZXInKQojIEJpb2NNYW5hZ2VyOjppbnN0YWxsKCdtc2lnZGJyJykKbGlicmFyeShjbHVzdGVyUHJvZmlsZXIpCmxpYnJhcnkobXNpZ2RicikKCm1fZGYgPC0gbXNpZ2RicihzcGVjaWVzID0gIkhvbW8gc2FwaWVucyIpCm1fdDJnIDwtIG1zaWdkYnIoc3BlY2llcyA9ICJIb21vIHNhcGllbnMiLCBjYXRlZ29yeSA9ICJDNSIsIHN1YmNhdGVnb3J5ID0gIkJQIikgICU+JSAKICBkcGx5cjo6c2VsZWN0KGdzX25hbWUsIGdlbmVfc3ltYm9sKQpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQptYXJrZXJfZ2VuZXNfdXAgPC0gbWFya2VyLmRmICU+JQogIGRwbHlyOjpmaWx0ZXIoYWRqLlAuVmFsX2NpcnIgPCAwLjA1ICYgbG9nRkNfY2lyciA+IDAuNSkgJT4lCiAgcHVsbChHZW5lSUQpIAoKbWFya2VyX2dlbmVzX2Rvd24gPC0gbWFya2VyLmRmICU+JQogIGRwbHlyOjpmaWx0ZXIoYWRqLlAuVmFsX2NpcnIgPCAwLjA1ICYgbG9nRkNfdW5pbmogPiAwLjUpICU+JQogIHB1bGwoR2VuZUlEKQoKZW1fdXAgPC0gZW5yaWNoZXIobWFya2VyX2dlbmVzX3VwLCBURVJNMkdFTkU9bV90MmcsIHBBZGp1c3RNZXRob2QgPSAiZmRyIiwgCiAgICAgICAgICAgICAgICAgIHVuaXZlcnNlID0gaHZncwogICAgICAgICAgICAgICAgICApCmVtX2Rvd24gPC0gZW5yaWNoZXIobWFya2VyX2dlbmVzX2Rvd24sIFRFUk0yR0VORT1tX3QyZywgcEFkanVzdE1ldGhvZCA9ICJmZHIiLCAKICAgICAgICAgICAgICAgICAgICB1bml2ZXJzZSA9IHJvd25hbWVzKGxpdmVyX21pbG8pCiAgICAgICAgICAgICAgICAgICAgKQoKZW1fcmVzX3VwIDwtIGVtX3VwQHJlc3VsdFtlbV91cEByZXN1bHQkcXZhbHVlIDwgMC4xLF0gJT4lCiAgZHBseXI6OnNlbGVjdCgtIGMoRGVzY3JpcHRpb24pKQplbV9yZXNfZG93biA8LSBlbV9kb3duQHJlc3VsdFtlbV9kb3duQHJlc3VsdCRxdmFsdWUgPCAwLjEsXSAlPiUKICBkcGx5cjo6c2VsZWN0KC0gYyhEZXNjcmlwdGlvbikpCmBgYAoKYGBge3IsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTE1LCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpnb19lbmRvX3VwIDwtIGVtX3Jlc191cCAlPiUKICB0b3BfbigzMCwgLWxvZzEwKHF2YWx1ZSkpICU+JQogICBtdXRhdGUoSUQ9aWZlbHNlKElEPT0nR09fQU5USUdFTl9QUk9DRVNTSU5HX0FORF9QUkVTRU5UQVRJT05fT0ZfUEVQVElERV9PUl9QT0xZU0FDQ0hBUklERV9BTlRJR0VOX1ZJQV9NSENfQ0xBU1NfSUknLCAiR09fQU5USUdFTl9QUkVTRU5UQVRJT05fVklBX01IQ19DTEFTU19JSSIsIElEKSkgJT4lCiAgbXV0YXRlKFRlcm09ZmFjdG9yKElELCBsZXZlbHM9cmV2KHVuaXF1ZShJRCkpKSkgJT4lCiAgZ2dwbG90KGFlcyhUZXJtLCAtbG9nMTAocXZhbHVlKSkpICsKICBnZW9tX3BvaW50KCkgKwogIGNvb3JkX2ZsaXAoKSArCiAgeGxhYigiR08gQmlvbG9naWNhbCBGdW5jdGlvbiIpICsgeWxhYigiLWxvZzEwKEFkai4gcC12YWx1ZSkiKSArCiAgdGhlbWVfYncoYmFzZV9zaXplPTE4KSArCiAgZ2d0aXRsZSgiQ2lycmhvdGljIGVuZG90aGVsaWEiKQoKZ29fZW5kb19kb3duIDwtIGVtX3Jlc19kb3duICU+JQogIHRvcF9uKDMwLCAtbG9nMTAocXZhbHVlKSkgJT4lCiAgbXV0YXRlKElEPWlmZWxzZShJRD09J0dPX0FOVElHRU5fUFJPQ0VTU0lOR19BTkRfUFJFU0VOVEFUSU9OX09GX1BFUFRJREVfT1JfUE9MWVNBQ0NIQVJJREVfQU5USUdFTl9WSUFfTUhDX0NMQVNTX0lJJywgIkdPX0FOVElHRU5fUFJFU0VOVEFUSU9OX1ZJQV9NSENfQ0xBU1NfSUkiLCBJRCkpICU+JQogIG11dGF0ZShUZXJtPWZhY3RvcihJRCwgbGV2ZWxzPXJldih1bmlxdWUoSUQpKSkpICU+JQogIGdncGxvdChhZXMoVGVybSwgLWxvZzEwKHF2YWx1ZSkpKSArCiAgZ2VvbV9wb2ludCgpICsKICBjb29yZF9mbGlwKCkgKwogIHhsYWIoIkdPIEJpb2xvZ2ljYWwgRnVuY3Rpb24iKSArIHlsYWIoIi1sb2cxMChBZGouIHAtdmFsdWUpIikgKwogIHRoZW1lX2J3KGJhc2Vfc2l6ZT0xOCkgKwogIGdndGl0bGUoIlVuaW5qdXJlZCBlbmRvdGhlbGlhIikKCmdvX2VuZG9fdXAKZ29fZW5kb19kb3duCmBgYAoKCmBgYHtyfQplbV9yZXNfdXAKZW1fcmVzX2Rvd24KYGBgCgojIyBDaG9sYW5naW9jeXRlcwoKYGBge3J9CnNldC5zZWVkKDQyKQptaWxvX3Jlc19jaG9sZ3JvdXBzIDwtIGdyb3VwTmhvb2RzKGxpdmVyX21pbG8sIG1pbG9fcmVzLCBtYXgubGZjLmRlbHRhID0gMC41LCBvdmVybGFwID0gMSkKCiMjIEdyb3VwIG5laWdoYm91cmhvb2RzIGJ5IERBIG91dGNvbWUKbWlsb19yZXNfY2hvbGdyb3VwcyROaG9vZEdyb3VwIDwtIE5BCm1pbG9fcmVzX2Nob2xncm91cHMkTmhvb2RHcm91cCA8LSBpZmVsc2UoKG1pbG9fcmVzX2Nob2xncm91cHMkYW5ub3RhdGlvbl9saW5lYWdlID09ICJDaG9sYW5naW9jeXRlcyIpICYgKG1pbG9fcmVzX2Nob2xncm91cHMkU3BhdGlhbEZEUiA8IDAuMSkgJiAobWlsb19yZXNfY2hvbGdyb3VwcyRsb2dGQyA8IC0yLjUpLCAiMzgiLCBtaWxvX3Jlc19jaG9sZ3JvdXBzJE5ob29kR3JvdXApCm1pbG9fcmVzX2Nob2xncm91cHMkTmhvb2RHcm91cCA8LSBpZmVsc2UoKG1pbG9fcmVzX2Nob2xncm91cHMkYW5ub3RhdGlvbl9saW5lYWdlID09ICJDaG9sYW5naW9jeXRlcyIpICYgKG1pbG9fcmVzX2Nob2xncm91cHMkU3BhdGlhbEZEUiA8IDAuMSkgJiAobWlsb19yZXNfY2hvbGdyb3VwcyRsb2dGQyA+IDIuNSksICI0OSIsIG1pbG9fcmVzX2Nob2xncm91cHMkTmhvb2RHcm91cCkKCmxpdmVyX21pbG8yIDwtIGxpdmVyX21pbG8Kc3Vic2V0Lm5ob29kcyA8LSBzdHJfZGV0ZWN0KG1pbG9fcmVzJGFubm90YXRpb25faW5kZXB0aCwgIkNob2wiKQpyZWR1Y2VkRGltKGxpdmVyX21pbG8yLCAiVU1BUCIpW2NvbG5hbWVzKGNob2xfbWlsbyksXSA8LSByZWR1Y2VkRGltKGNob2xfbWlsbywgIlVNQVAiKSAKcGxvdE5ob29kR3JvdXBzKGxpdmVyX21pbG8yLCBtaWxvX3Jlc19jaG9sZ3JvdXBzW21pbG9fcmVzX2Nob2xncm91cHMkYW5ub3RhdGlvbl9saW5lYWdlPT0iQ2hvbGFuZ2lvY3l0ZXMiLF0sIAogICAgICAgICAgICAgICAgc2hvd19ncm91cHMgPSBjKCI0OSIsIjM4IiksCiAgICAgICAgICAgICAgICBzdWJzZXQubmhvb2RzID0gIG1pbG9fcmVzX2Nob2xncm91cHMkYW5ub3RhdGlvbl9saW5lYWdlID09IkNob2xhbmdpb2N5dGVzIikKCmBgYAoKQ2FsY3VsYXRlIG1hcmtlciBnZW5lcyBiZXR3ZWVuIHRoZSB0d28gZ3JvdXBzCmBgYHtyfQojIyBGaWx0ZXIgZ2VuZXMgZXhwcmVzc2VkIGluIGNob2xhbmdpb2N5dGVzCiMgY2hvbF9odmdzIDwtIGh2Z3NbKGNvdW50cyhjaG9sX21pbG8pW2h2Z3MsXSA+IDApICU+JSB7cm93U3VtcyguKS9uY29sKGNob2xfbWlsbyl9ID4gMC4wMV0KbWl0b19nZW5lcyA8LSBzdHJfZGV0ZWN0KGh2Z3MsICJeTVQtIikKCm1hcmtlcnNfZGYgPC0gZmluZE5ob29kR3JvdXBNYXJrZXJzKGxpdmVyX21pbG8sIGRhLnJlcyA9IG1pbG9fcmVzX2Nob2xncm91cHMsIGFzc2F5PSJjb3VudHMiLAogICAgICAgICAgICAgICAgICAgICAgc3Vic2V0Lm5ob29kcyA9IG1pbG9fcmVzX2Nob2xncm91cHMkTmhvb2RHcm91cCAlaW4lYygiNDkiLCIzOCIpLAogICAgICAgICAgICAgICAgICAgICAgc3Vic2V0Lmdyb3VwcyA9IGMoIjQ5IiwiMzgiKSwKICAgICAgICAgICAgICAgICAgICAgIHN1YnNldC5yb3cgPSBodmdzWyFtaXRvX2dlbmVzXSwKICAgICAgICAgICAgICAgICAgICAgIGFnZ3JlZ2F0ZS5zYW1wbGVzID0gVFJVRSwgc2FtcGxlX2NvbCA9ICJkYXRhc2V0IgogICAgICAgICAgICAgICAgICAgICAgKQoKbWFya2Vyc19kZiAKCm1pbG9fcmVzX2Nob2xncm91cHNbbWlsb19yZXNfY2hvbGdyb3VwcyROaG9vZEdyb3VwICVpbiVjKCI0OSIsIjM4IiksXQpgYGAKCiMjIyMgVmlzdWFsaXplIGFzIHZvbGNhbm8gCgpgYGB7ciwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTAsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9Cm1hcmtlci5kZi5jaG9sIDwtIG1hcmtlcnNfZGYKCnZvbGNhbm9fY2hvbCA8LQogIG1hcmtlci5kZi5jaG9sICU+JQogIG11dGF0ZSh1cD1pZmVsc2UobG9nRkNfNDkgPiAwLCAidXAiLCAiZG93biIpKSAlPiUKICBncm91cF9ieSh1cCkgJT4lCiAgbXV0YXRlKGxhYmVsPWlmZWxzZShyYW5rKGFkai5QLlZhbF80OSkgPCAxNSwgR2VuZUlELCBOQSkpICU+JQogICMgbXV0YXRlKGxhYmVsPWlmZWxzZSgoYWRqLlAuVmFsXzQ5IDwgMC4wNSAmIGxvZ0ZDXzQ5IDwgLTMpIHwgKGFkai5QLlZhbF80OSA8IDAuMDUgJiBsb2dGQ180OSA+IDApLCBHZW5lSUQsIE5BKSkgJT4lCiAgZ2dwbG90KGFlcyhsb2dGQ180OSwgLWxvZzEwKGFkai5QLlZhbF80OSksIAogICAgICAgICAgICAgIyBjb2xvcj1oaWdobGlnaHQKICAgICAgICAgICAgICkpICsgCiAgZ2VvbV9wb2ludChzaXplPTAuOCwgYWxwaGE9MC42KSArCiAgZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsKGFlcyhsYWJlbD1sYWJlbCksIHNlZ21lbnQuYWxwaGEgPSAwLjIpICsKICB4bGFiKCJsb2dGQyIpICsgeWxhYigiLSBsb2cxMChBZGouIFAgdmFsdWUpIikgKwogIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDIyKQoKdm9sY2Fub19jaG9sICAKICAKYGBgCgoKCiMjIyBHTyB0ZXJtIGFuYWx5c2lzCgpgYGB7ciwgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KbWFya2VyX2dlbmVzX2Nob2wgPC0gbWFya2VyLmRmLmNob2wgJT4lCiAgZHBseXI6OmZpbHRlcihhZGouUC5WYWxfNDkgPCAwLjA1ICYgbG9nRkNfNDkgPiAwKSAlPiUKICBwdWxsKEdlbmVJRCkKCmVtX3VwX2Nob2wgPC0gZW5yaWNoZXIobWFya2VyX2dlbmVzX2Nob2wsIFRFUk0yR0VORT1tX3QyZywgcEFkanVzdE1ldGhvZCA9ICJmZHIiLCAKICAgICAgICAgICAgICAgICAgdW5pdmVyc2UgPSByb3duYW1lcyhsaXZlcl9taWxvKQogICAgICAgICAgICAgICAgICApCgplbV9yZXNfdXBfY2hvbCA8LSBlbV91cF9jaG9sQHJlc3VsdFtlbV91cF9jaG9sQHJlc3VsdCRxdmFsdWUgPCAwLjEsXSAlPiUKICBkcGx5cjo6c2VsZWN0KC0gYyhEZXNjcmlwdGlvbikpCmBgYAoKYGBge3IsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTE1LCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpnb19jaG9sX3VwIDwtIGVtX3Jlc191cF9jaG9sICU+JQogIHRvcF9uKDIwLCAtbG9nMTAocXZhbHVlKSkgJT4lCiAgbXV0YXRlKFRlcm09ZmFjdG9yKElELCBsZXZlbHM9cmV2KHVuaXF1ZShJRCkpKSkgJT4lCiAgZ2dwbG90KGFlcyhUZXJtLCAtbG9nMTAocXZhbHVlKSkpICsKICBnZW9tX3BvaW50KCkgKwogIGNvb3JkX2ZsaXAoKSArCiAgeGxhYigiR08gQmlvbG9naWNhbCBGdW5jdGlvbiIpICsgeWxhYigiLWxvZzEwKEFkai4gcC12YWx1ZSkiKSArCiAgdGhlbWVfYncoYmFzZV9zaXplPTE4KSArCiAgZ2d0aXRsZSgiQ2lycmhvdGljIGNob2xhbmdpb2N5dGVzIikKCmdvX2Nob2xfdXAKYGBgCgpgYGB7cn0KZW1fcmVzX3VwX2Nob2wKYGBgCmBgYHtyLCBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQptYXJrZXJfZ2VuZXNfY2hvbF9kb3duIDwtIG1hcmtlci5kZi5jaG9sICU+JQogIGRwbHlyOjpmaWx0ZXIoYWRqLlAuVmFsXzQ5IDwgMC4wNSAmIGxvZ0ZDXzQ5IDwgMCkgJT4lCiAgcHVsbChHZW5lSUQpCgplbV9kb3duX2Nob2wgPC0gZW5yaWNoZXIobWFya2VyX2dlbmVzX2Nob2xfZG93biwgVEVSTTJHRU5FPW1fdDJnLCBwQWRqdXN0TWV0aG9kID0gImZkciIsIAogICAgICAgICAgICAgICAgICB1bml2ZXJzZSA9IHJvd25hbWVzKGxpdmVyX21pbG8pCiAgICAgICAgICAgICAgICAgICkKCmVtX3Jlc19kb3duX2Nob2wgPC0gZW1fZG93bl9jaG9sQHJlc3VsdFtlbV9kb3duX2Nob2xAcmVzdWx0JHF2YWx1ZSA8IDAuMSxdICU+JQogIGRwbHlyOjpzZWxlY3QoLSBjKERlc2NyaXB0aW9uKSkKYGBgCgoKLS0tCgpBc3NlbWJsZSBmaWd1cmUKYGBge3IsIGZpZy5oZWlnaHQ9MjUsIGZpZy53aWR0aD0xOX0KZmlnNF9ib3R0b20gPC0gKChmaWc0X2JsZWZ0ICsgcGxvdF9sYXlvdXQoKSkgfAogICAgICAoKGZpZzRfYnJpZ2h0MSArIHBsb3RfbGF5b3V0KHRhZ19sZXZlbCA9ICdrZWVwJykpIC8gKGZpZzRfYmJyaWdodCArIHBsb3RfbGF5b3V0KCkpKSArCiAgICAgIHBsb3RfbGF5b3V0KGhlaWdodHMgPSBjKDEsMS42KSkKICAgKSArCiAgcGxvdF9sYXlvdXQod2lkdGhzPWMoMSwxLjQpKQoKKGZpZzRfdG9wIC8gZmlnNF9ib3R0b20pICsKICBwbG90X2xheW91dChoZWlnaHRzPWMoMSwxLjgpKSAgKwogIGdnc2F2ZSgifi9tb3VudC9nZHJpdmUvbWlsby9GaWd1cmVzL2xpdmVyX3YyL2ZpZzRfcmF3LnBkZiIsIGhlaWdodCA9IDI2LCB3aWR0aCA9IDI0LCB1c2VEaW5nYmF0cz1GQUxTRSkgCiAgIyBnZ3NhdmUoIn4vbW91bnQvZ2RyaXZlL21pbG8vRmlndXJlcy9saXZlcl92Mi9maWc0X3Jhdy5wbmciLCBoZWlnaHQgPSAyNiwgd2lkdGggPSAyNCwgdXNlRGluZ2JhdHM9RkFMU0UpCiAgIyBnZ3NhdmUoIn4vbWlsby9tcy9maWd1cmVzL2ZpZ3MvZmlndXJlNS5wZGYiLCBoZWlnaHQgPSAyNiwgd2lkdGggPSAyMiwgdXNlRGluZ2JhdHM9RkFMU0UpCmBgYAoKQXNzZW1ibGUgc3VwcGxlbWVudGFyeSBmaWd1cmUKCmBgYHtyLCBmaWcud2lkdGg9MjUsIGZpZy5oZWlnaHQ9N30KcDEgPC0gcGxvdF9ncmlkKCBnb19lbmRvX3VwKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMSkpLCAKICAgICAgICAgICAgICAgICBnb19lbmRvX2Rvd24rIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDEpKSwgCiAgICAgICAgICAgICAgICAgbGFiZWxfc2l6ZSA9IDE4LAogICAgICAgICAgICAgICAgIG5jb2w9MSwKICAgICAgICAgICAgICAgICByZWxfaGVpZ2h0cyA9IGMoMiwyKSwKICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIkEiLCAiQiIsIkMiKSkKCnAxCgpjaG9sX2VtYiA8LSAoY2hvbF91bWFwICsgY2hvbF9nciApICsgCiAgcGxvdF9sYXlvdXQod2lkdGhzID0gYygxLDIpLCAKICAgICAgICAgICAgICAgIGd1aWRlcyA9ICJjb2xsZWN0IgogICAgICAgICAgICAgICAgKQoKYGBgCgpgYGB7ciwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTh9CnBsb3RfZ3JpZCgKICBnb19lbmRvX3VwKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMSkpLCAKICAgICAgICAgICAgICAgICBnb19lbmRvX2Rvd24rIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDEpKSwgCiAgICAgICAgICAgICAgICAgbGFiZWxfc2l6ZSA9IDE4LAogICAgICAgICAgICAgICAgIG5jb2w9MSwKICAgICAgICAgICAgICAgICByZWxfaGVpZ2h0cyA9IGMoMiwyKSwgcmVsX3dpZHRocyA9IGMoMiwyKSwKICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIkEiLCAiQiIpCiAgKSArCiAgZ2dzYXZlKCJ+L21vdW50L2dkcml2ZS9taWxvL0ZpZ3VyZXMvbGl2ZXJfdjIvc3VwcGxfZmlnX2VuZG8ucGRmIiwgaGVpZ2h0ID0gMTIsIHdpZHRoPTEyKSArCiAgZ2dzYXZlKCJ+L21vdW50L2dkcml2ZS9taWxvL0ZpZ3VyZXMvbGl2ZXJfdjIvc3VwcGxfZmlnX2VuZG8ucG5nIiwgaGVpZ2h0ID0gMTIsIHdpZHRoPTEyKQoKCmBgYApgYGB7ciwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTh9CnBsb3RfZ3JpZChwbG90X2dyaWQoY2hvbF91bWFwLCBjaG9sX2dyLCB2b2xjYW5vX2Nob2wsIG5yb3c9MSxyZWxfd2lkdGhzID0gYygxLDIsMiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxfc2l6ZSA9IDE4LAogICAgICAgICAgICAgICAgbGFiZWxzID0gYygiQSIsIkIiLCJDIikpLAogICAgICAgICAgICAgICAgZ29fY2hvbF91cCArIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoaGp1c3QgPSAxKSksIAogICAgICAgICAgICAgICAgbmNvbD0xLAogICAgICAgICAgICAgICAgcmVsX2hlaWdodHMgPSBjKDEsMSksCiAgICAgICAgICAgICAgICAgbGFiZWxfc2l6ZSA9IDE4LAogICAgICAgICAgICAgICAgbGFiZWxzPWMoIiIsJ0QnKSkgKwogICBnZ3NhdmUoIn4vbW91bnQvZ2RyaXZlL21pbG8vRmlndXJlcy9saXZlcl92Mi9zdXBwbF9maWc3LnBkZiIsIGhlaWdodCA9IDEzLCB3aWR0aD0xNCkgKwogIGdnc2F2ZSgifi9tb3VudC9nZHJpdmUvbWlsby9GaWd1cmVzL2xpdmVyX3YyL3N1cHBsX2ZpZzcucG5nIiwgaGVpZ2h0ID0gMTMsIHdpZHRoPTE0KSAKYGBgCg==